"""Integrated Agent flow: Firebird read -> Hub /v1/classify/batch -> IPI dry-run -> guarded auto-apply write. FIREBIRD_WRITES=0 (default): classifies every product and logs the decision (and what write *would* happen) to the local SQLite audit log; never writes. FIREBIRD_WRITES=1: also auto-applies every write_eligible C000025.IPI proposal where rate_kind=NUMERIC, reusing the same schema/path/allowlist/compare-before-write guards as the manual executor. Fails closed before touching the connection unless the configured database is exactly the authorized disposable test database. Auto-apply is scoped to table=C000025, field=IPI, rate_kind=NUMERIC only. NT/ non-numeric markers, blank rates, ambiguous EX candidates, legacy/invalid NCM, CEST and EX_TIPI are never auto-applied here. """ import argparse import importlib.util import json import os import sys from pathlib import Path AGENT_ROOT = Path(__file__).resolve().parents[1] PROJECT_ROOT = AGENT_ROOT.parent sys.path.insert(0, str(AGENT_ROOT / "src")) from gti_fiscal_agent.client import HubClient # noqa: E402 from gti_fiscal_agent.firebird_reader import read_products # noqa: E402 from gti_fiscal_agent.pipeline import run_pipeline # noqa: E402 from gti_fiscal_agent.runner import REFERENCE_SCHEMA_V1, to_hub_product # noqa: E402 from gti_fiscal_agent.state import AgentState # noqa: E402 def load_dotenv(path: Path) -> None: if not path.exists(): return for line in path.read_text(encoding="utf-8").splitlines(): line = line.strip() if not line or line.startswith("#") or "=" not in line: continue key, _, value = line.partition("=") os.environ.setdefault(key.strip(), value.strip()) def load_executor(): spec = importlib.util.spec_from_file_location("firebird_write_executor", PROJECT_ROOT / "scripts" / "firebird_write_executor.py") module = importlib.util.module_from_spec(spec) sys.modules[spec.name] = module spec.loader.exec_module(module) return module def main(): parser = argparse.ArgumentParser() parser.add_argument("--hub-url", default=os.getenv("HUB_URL", "http://localhost:8000")) parser.add_argument("--token", default=os.getenv("AGENT_DEV_TOKEN", "")) parser.add_argument("--state-db", default=str(AGENT_ROOT / "agent_state.sqlite")) parser.add_argument("--schema-fingerprint", default=REFERENCE_SCHEMA_V1) args = parser.parse_args() load_dotenv(PROJECT_ROOT / ".env") if not args.token: args.token = os.environ.get("AGENT_DEV_TOKEN", "") firebird_writes = os.environ.get("FIREBIRD_WRITES", "0") == "1" executor = load_executor() database_path = os.environ["FIREBIRD_DATABASE_PATH"] if firebird_writes: # Fail closed before any product is read or any connection is opened for writing. executor.assert_authorized_write_path(database_path) if args.schema_fingerprint != REFERENCE_SCHEMA_V1: print(json.dumps({"status": "SCHEMA_UNKNOWN", "processed": 0})) return products = read_products() client = HubClient(args.hub_url, args.token) decisions = client.classify_batch([to_hub_product(p) for p in products]) state = AgentState(args.state_db) write_connection = None if firebird_writes: import fdb write_connection = fdb.connect( database=database_path, user=os.environ["FB_USER"], password=os.environ["FB_PASSWORD"], fb_library_name=os.environ.get("FIREBIRD_CLIENT_LIBRARY"), ) try: summary = run_pipeline(products, decisions, state, firebird_writes, write_connection, executor, database_path) finally: if write_connection is not None: write_connection.close() summary = {"status": "OK", "firebird_writes_enabled": firebird_writes, **summary} print(json.dumps(summary, indent=2)) if __name__ == "__main__": main()