"""COPY-ONLY write E2E for the RTC auto-apply (C000025.CCLASSTRIB). python scripts/rtc_copy_write_e2e.py Runs ONLY against scratch_rtc_e2e/TESTESOFICIAIS_RTC_COPY.FDB (a gbak restore of TESTESOFICIAIS). It refuses any other path, and refuses every real database. Scenario (all through the Agent's own code path `run_agent_service._rtc_auto_apply`): 1. the copy's company is switched to regime normal (C000004.CRT='3'): a disposable experiment, so the rules can act (the real company is Simples => 0 candidates); 2. assess -> candidates; 3. apply with all three gates open; 4. re-read, FK/CST checks, coverage; 5. ONLY CCLASSTRIB changed (whole-table diff); 6. second run: 0 candidates, 0 writes; 7. stale-decision abort (RTC switched off on the copy between decision and write) writes nothing. Writes go to the copy and nowhere else. """ import importlib.util import json import os import sys from datetime import date from pathlib import Path from types import SimpleNamespace ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT)); sys.path.insert(0, str(ROOT / "scripts")); sys.path.insert(0, str(ROOT / "agent" / "src")) from dotenv import load_dotenv # noqa: E402 import fdb # noqa: E402 import local_completion as lc # noqa: E402 from central.app.operational_state import classify_product_state, summarize_operational_states # noqa: E402 from gti_fiscal_agent import firebird_reader, unified_fiscal # noqa: E402 COPY = str((ROOT / "scratch_rtc_e2e" / "TESTESOFICIAIS_RTC_COPY.FDB").resolve()) NCM_OK = set((ROOT / "docs" / "runs" / "ncm_valid_codes.txt").read_text().split()) def guard(path): canon = os.path.normcase(os.path.abspath(path)) if canon != os.path.normcase(COPY): raise SystemExit(f"REFUSED: {path} is not the disposable copy") real = {os.path.normcase(os.path.abspath(p)) for p in lc.allowlist().values()} if canon in real or "scratch_rtc_e2e" not in canon: raise SystemExit("REFUSED: real database") def connect(rw): kw = dict(database=COPY, user=os.environ["FB_USER"], password=os.environ["FB_PASSWORD"], fb_library_name=os.environ.get("FIREBIRD_CLIENT_LIBRARY")) if not rw: kw["isolation_level"] = fdb.ISOLATION_LEVEL_READ_COMMITED_RO return fdb.connect(**kw) def load_service(): spec = importlib.util.spec_from_file_location("run_agent_service", ROOT / "agent" / "scripts" / "run_agent_service.py") mod = importlib.util.module_from_spec(spec) sys.modules[spec.name] = mod spec.loader.exec_module(mod) return mod def digits(v): return "".join(ch for ch in str(v or "") if ch.isdigit()) def snapshot_table(cur, table, key, skip_cols=()): cur.execute(f"SELECT * FROM {table}") cols = [d[0] for d in cur.description] ki = cols.index(key) return cols, {r[ki]: {c: v for c, v in zip(cols, r) if c not in skip_cols} for r in cur.fetchall()} def coverage(env, company, ncm_valid, override=None): conn = firebird_reader._connect(env) try: out = unified_fiscal.assess_from_connection(conn, company, ncm_valid=ncm_valid, rtc_override=override) finally: conn.close() products = firebird_reader.read_products(env) states = [classify_product_state(p, {"status": "EXACT_NCM"}, fiscal=out["findings"].get(str(p["CODIGO"]))) for p in products] s = summarize_operational_states(states) return out, {"total": s["TOTAL_PRODUCTS"], "complete": s["COMPLETE_PRODUCTS"], "blocked": s["SAFE_BLOCKED"], "unclassified": s["UNCLASSIFIED"], "coverage_percent": round(100 * s["COMPLETE_PRODUCTS"] / s["TOTAL_PRODUCTS"], 2)} def main(): load_dotenv(ROOT / ".env") # credentials only when the script runs, never as an import side effect guard(COPY) service = load_service() env = {"FIREBIRD_DATABASE_PATH": COPY, "FB_USER": os.environ["FB_USER"], "FB_PASSWORD": os.environ["FB_PASSWORD"], "FIREBIRD_CLIENT_LIBRARY": os.environ.get("FIREBIRD_CLIENT_LIBRARY")} report = {"database": "scratch_rtc_e2e/TESTESOFICIAIS_RTC_COPY.FDB (gbak restore of TESTESOFICIAIS)", "real_database_writes": 0, "real_database_write_attempts": 0} # ---- 1. company -> regime normal on the COPY (documented experiment) rw = connect(True); cur = rw.cursor() cur.execute("SELECT DISTINCT TRIM(CRT) FROM C000004"); report["copy_original_crt"] = [r[0] for r in cur.fetchall()] cur.execute("UPDATE C000004 SET CRT = '3'"); rw.commit() cur.execute("SELECT DISTINCT TRIM(CRT) FROM C000004"); company = {"crt": cur.fetchone()[0]} report["copy_crt_for_experiment"] = company["crt"] _, before_c25 = snapshot_table(cur, "C000025", "CODIGO") cur.execute("SELECT USAR_REFORMA_TRIBUTARIA FROM PARAMETROS"); report["copy_rtc_param"] = cur.fetchone()[0] rw.commit(); rw.close() ncm_valid = {str(c).strip(): digits(v["CLASSIFICACAO_FISCAL"]) in NCM_OK for c, v in before_c25.items()} # ---- 2. assess (as found on the copy) out, cov_before = coverage(env, company, ncm_valid) report["coverage_before"] = cov_before report["resolution_before"] = {k: v for k, v in out["context"]["rtc_resolution"].items() if k != "by_reason"} products = firebird_reader.read_products(env) config = SimpleNamespace(firebird_writes=True, rtc_auto_apply_enabled=True, rtc_auto_apply_max_per_job=500, database_path=COPY) job = {"job_id": 424242, "authorizations": {"rtc_auto_apply": True}} # ---- 7 (first, on purpose): stale decision - RTC switched off on the copy between decision and write => nothing is written rw = connect(True); c = rw.cursor(); c.execute("UPDATE PARAMETROS SET USAR_REFORMA_TRIBUTARIA = 0"); rw.commit(); rw.close() stale = service._rtc_auto_apply(env, config, job, products, out, company) report["stale_decision_run"] = {"write_attempts": stale["write_attempts"], "write_successes": stale["write_successes"], "aborted_reasons": stale["aborted_reasons"]} rw = connect(True); c = rw.cursor(); c.execute("UPDATE PARAMETROS SET USAR_REFORMA_TRIBUTARIA = 1"); rw.commit(); rw.close() # ---- 3. apply first = service._rtc_auto_apply(env, config, job, products, out, company) report["first_run"] = {"mode": first["mode"], "ready_total": first["ready_total"], "write_attempts": first["write_attempts"], "write_successes": first["write_successes"], "aborted_reasons": first["aborted_reasons"], "audit_sample": first["applied"][:3]} # ---- 4/5. re-read, FK, whole-table diff ro = connect(False); cur = ro.cursor() cols, after_c25 = snapshot_table(cur, "C000025", "CODIGO") changed = {code: [c for c in cols if before_c25[code][c] != row[c]] for code, row in after_c25.items() if any(before_c25[code][c] != row[c] for c in cols)} report["rows_changed"] = len(changed) report["columns_changed"] = sorted({c for v in changed.values() for c in v}) cur.execute("SELECT COUNT(*) FROM C000025 p WHERE p.CCLASSTRIB IS NOT NULL AND NOT EXISTS (SELECT 1 FROM RTC_CCLASSTRIB c WHERE c.CCLASSTRIB = p.CCLASSTRIB)"); report["fk_orphans"] = cur.fetchone()[0] cur.execute("SELECT COUNT(*) FROM C000025 p JOIN RTC_CCLASSTRIB c ON c.CCLASSTRIB = p.CCLASSTRIB WHERE NOT EXISTS (SELECT 1 FROM RTC_CST s WHERE s.CSTIBSCBS = c.CSTIBSCBS)"); report["cst_orphans"] = cur.fetchone()[0] cur.execute("SELECT CCLASSTRIB, COUNT(*) FROM C000025 WHERE CCLASSTRIB IS NOT NULL GROUP BY 1"); report["written_by_class"] = {r[0].strip(): r[1] for r in cur.fetchall()} cur.execute("SELECT COUNT(*) FROM RTC_CCLASSTRIB"); report["catalog_rows_unchanged"] = cur.fetchone()[0] ro.close() out2, cov_after = coverage(env, company, ncm_valid) report["coverage_after"] = cov_after report["resolution_after"] = {k: v for k, v in out2["context"]["rtc_resolution"].items() if k != "by_reason"} # ---- 6. second run products2 = firebird_reader.read_products(env) second = service._rtc_auto_apply(env, config, job, products2, out2, company) report["second_run"] = {"ready_total": second["ready_total"], "write_attempts": second["write_attempts"], "write_successes": second["write_successes"]} report["IDEMPOTENT"] = second["write_attempts"] == 0 and second["write_successes"] == 0 report["ONLY_CCLASSTRIB_CHANGED"] = report["columns_changed"] in ([], ["CCLASSTRIB"]) report["PASS"] = bool(report["IDEMPOTENT"] and report["ONLY_CCLASSTRIB_CHANGED"] and report["fk_orphans"] == 0 and report["cst_orphans"] == 0 and first["write_successes"] == first["write_attempts"] > 0 and report["stale_decision_run"]["write_successes"] == 0 and report["rows_changed"] == first["write_successes"]) (ROOT / "docs" / "runs" / "rtc_copy_write_e2e.json").write_text(json.dumps(report, ensure_ascii=False, indent=1, default=str), encoding="utf-8") print(json.dumps({k: report[k] for k in ("copy_original_crt", "copy_rtc_param", "coverage_before", "coverage_after", "first_run", "stale_decision_run", "rows_changed", "columns_changed", "fk_orphans", "cst_orphans", "second_run", "IDEMPOTENT", "ONLY_CCLASSTRIB_CHANGED", "PASS")}, ensure_ascii=False, default=str)[:3000]) if __name__ == "__main__": main()