Files
GTI_GERENCIADOR_FISCAL/docs/AI_PROJECT_STATE.md
T
gtiandClaude Sonnet 5 8d6122c096 feat: Agent distribution with secure enrollment from the control panel
One universal Windows Agent package per version (agent_releases: version/
platform/architecture/sha256/status DRAFT|PUBLISHED|REVOKED; the binary
lives on a local release-storage volume, never in PostgreSQL). An operator
creates a pending installation (agent_enrollments) from Agents / Servidores
-> + Nova instalação, then either downloads it directly or generates a
one-time HTTPS link (short TTL, single use). Either path streams the
package plus a freshly minted enrollment.json whose token is never written
to storage in plaintext, only its SHA-256 hash. On the client host,
gti-agent.ps1 install-auto verifies the bundle's SHA-256, refuses a non-
HTTPS Hub, enrolls via POST /v1/agents/enroll (deleting the enrollment
token immediately after, success or failure), writes an ACL-restricted
.env with the newly issued permanent Agent token, and registers the
Windows Service without starting it (Firebird stays unconfigured on
purpose). Idempotent, replay-proof, rate-limited per IP, fully audited,
never logs a secret.

Migration 0022_agent_releases_enrollment (verified fresh-DB ==
upgrade-from-0021 by pg_dump diff). GTI_FISCAL_VERSION/AGENT_VERSION
bumped to 1.1.0 as the single source of truth across code, packages, the
release manifest and every published AgentRelease row.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-23 13:19:31 -03:00

39 KiB
Raw Blame History

AI Project State

Agent distribution + secure enrollment V1 (2026-09-23)

AGENT_DISTRIBUTION_V1_IMPLEMENTED — the Windows Agent is now installable end to end from the control panel instead of a manual register_agent.py + hand-copied .env. One universal package per version (agent_releases: version/platform/architecture/sha256/status DRAFT|PUBLISHED|REVOKED, binary on a local agent_releases volume, never in PostgreSQL, published by central/scripts/publish_agent_release.py, never a browser upload). Agents / Servidores -> + Nova instalação creates a pending installation (agent_enrollments); its page offers an authenticated direct download or a one-time HTTPS link (/dl/agent/{token}, single use, short TTL). Either path streams the package plus a freshly minted enrollment.json — a one-time enrollment token whose plaintext is never written to storage, only its SHA-256 hash. On the client host, gti-agent.ps1 install-auto verifies the bundle's SHA-256 against MANIFEST.json, refuses a non-HTTPS Hub, calls POST /v1/agents/enroll (deletes enrollment.json immediately after either outcome), writes an ACL-restricted .env with the newly issued permanent Agent token, and registers the Windows Service without starting it (Firebird stays unconfigured on purpose — the operator fills it in, then preflight/start). Idempotent re-run, replay-proof (the enrollment token's hash is cleared on use), rate-limited per IP (central/app/rate_limit.py), fully audited (INSTALLATION_CREATED/DOWNLOAD_LINK_CREATED/PACKAGE_DOWNLOADED/ ENROLLMENT_SUCCEEDED/ENROLLMENT_REJECTED/AGENT_CREDENTIAL_ISSUED/ INSTALLATION_REVOKED, real IP, never a token). Migration 0022_agent_releases_enrollment; verified fresh-DB == upgrade-from-0021 (pg_dump identical) on both local dev and production Postgres. Real end-to-end proof ran over HTTP against the running Hub (not just TestClient): create -> link -> single-use download -> tamper/replay rejection -> enroll -> the minted Agent token authenticates GET /v1/datasets/status. GTI_FISCAL_VERSION/AGENT_VERSION bumped to 1.1.0 (single source of truth: release/VERSION, both pyproject.toml, run_agent_service.py's default, the release manifest and every published AgentRelease row). See docs/PILOT_DEPLOYMENT.md, "Agent distribution: install from the panel". No real client Agent was installed by this pass — FIRST_REAL_AGENT_INSTALLED=false; the operator chooses the first pilot.

Autonomous operations closure V1 (2026-09-22)

AUTONOMOUS_OPERATIONS_CLOSURE_VALIDATED — the Agent now models per-product operational states, keeps safe blocked products in a deduplicated Hub registry, retries only when input evidence/context changes, and auto-resolves blockers after a later successful evaluation. Partial processing keeps heartbeat and new jobs healthy. AUTONOMOUSLY_PROCESSABLE_PRODUCTS=35066 despite SAFE_BLOCKED_PRODUCTS=194; no AI or Firebird write is required for operation.

Control panel V1 foundation (2026-09-22)

CONTROL_PANEL_FOUNDATION_IMPLEMENTED — the Hub now has persistent installation identity, enriched metadata, snapshots, deterministic health/version drift, redacted structured events with retention, separate human admin sessions with CSRF, paginated control-plane APIs, and Jinja2 operator screens. The panel does not expose Agent tokens as human sessions and cannot enable AI/Firebird writes, execute SQL, or run shell commands.

Autonomous fiscal completion V1 (2026-09-22)

AUTONOMOUS_FISCAL_COMPLETION_V1_MEASURED — requiredness matrix and pure FiscalCompletenessEvaluator are active; read-only inbound discovery covered all five allowlisted databases and produced ERP_INBOUND_FISCAL_EVIDENCE_MAP_V1 plus supplier observation metrics. Origin, role, ST/withheld, and entry scope remain evidence-gated. No human decisions were answered, no NCM conflicts were resolved, no external product provider was queried, and no Firebird write was executed. The central ai_usage_ledger and external_product_cache schema objects are defined by migration 0015, with offline budget and idempotency guards. Measured matrix completeness is 34,872/35,066 products; AI residual is unchanged at 511. AI_WRITE_ALLOWED=false, FIREBIRD_AI_WRITES=0, retrieval V2 remains active, and retrieval V3 remains unpromoted.

Current status (real LLM evaluation V1 — 2026-09-21)

BLOCKED_EXTERNAL_AI_CREDENTIAL (implementation complete). The entire real-LLM evaluation pipeline is built and tested offline: OpenAI-compatible provider (central/app/ai/llm_provider.py, key only from Hub env, injectable transport), strict per-item validator (candidate-list confinement, abstain semantics, 1:1 work_item_id, calibrated confidence in [0,1]), progressive batch probing (20/10/5), post-AI validator (active-dataset membership), metrics + A/B/C error attribution + confidence-bucket calibration (central/app/ai/eval_metrics.py), permanent result cache (ai_eval_cache, migration 0014) with zero-call re-execution proof, and the runner central/scripts/run_real_llm_ncm_eval.py (gold-first; silver/noisy only after the gate: selection accuracy >= 0.95 AND high-confidence wrong rate <= 0.02; artifacts REAL_LLM_NCM_EVALUATION_V1/ERRORS/COST). The runner refuses to call anything without AI_API_KEY and exits BLOCKED_EXTERNAL_AI_CREDENTIAL with the exact operator action. Zero LLM calls, zero Firebird writes, AI_WRITE_ALLOWED=false. Operator action: set AI_API_KEY (optionally AI_BASE_URL/AI_MODEL) in the Hub .env.

Semantic candidate retrieval V2 (2026-09-21)

NCM_RETRIEVAL_V2_GATE_PASSED. Deterministic hybrid retrieval (hierarchical official corpus enriched with ancestor descriptions + KB commercial->NCM neighbor bridge + KB-derived abbreviation lexicon, 644 entries; weights 0.3/0.7; leave-one-out discipline after catching self-retrieval leakage) lifted candidate recall from 7.5%@50 (V1) to HYBRID 92%@20 / 93.5%@50, GOLD 95.4%@20/@50, MRR 0.84 — meeting the LLM liberation gate (GOLD@20 ≥ 0.80, @50 ≥ 0.90). Benchmark quality audit: GOLD 65 / SILVER 118 / NOISY 17. GTIN audit: 8,559 valid, 251 cross-tenant matches, 90 GTIN-conflicting NCMs surfaced. Reprocessing the 750 work items: 4 KB_EXACT + 16 KB_FINGERPRINT + 1 high-confidence retrieval + 143 single-official-candidate (CEST/EX) resolved deterministically; 511 items (4,181 cells) still need LLM selection; projected cost $0.54 → $0.44. Zero LLM calls, zero Firebird writes, AI_WRITE_ALLOWED remains false. Artifacts: docs/NCM_RETRIEVAL_EVALUATION_V2.json, docs/NCM_RETRIEVAL_ARCHITECTURE_V2.md, docs/GTIN_KB_AUDIT_V1.json, docs/AI_RESIDUAL_PLAN_V2.json.

AI semantic layer V1 (2026-09-21)

AI_SEMANTIC_LAYER_V1_IMPLEMENTED. The deterministic gate was closed (OFFICIAL_RULE_RESOLVABLE_REMAINING=0, ERP_DATA_RESOLVABLE_REMAINING=0) and the Hub now owns a Product Knowledge Base plus a deduplicated AI work-item queue. entry_scope now admits MIXED_OR_DYNAMIC (CFOP_ENTRADA becomes operation-dependent; no CFOP invented). KB seeded read-only from all 5 databases: 30,801 global products (8,559 GTIN), 35,065 observations, 163 surfaced knowledge conflicts, idempotent load. 850 residual questions deduped into 750 work items (20 answered by KB cache alone). Budget-guarded cost plan: $0.54 estimated for 730 items (authorized, $0 spent — no AI credential exists here). The 200-sample NCM benchmark proved the honest floor: candidate recall@10 = 5.5% (ERP commercial wording vs official legal wording), so AI selection over closed candidates is capped there — READY_FOR_AI_ASSISTED_WRITE_V2=false with candidate generation as the named prerequisite. AI_WRITE_ALLOWED=false in V1: AI results stay AI_CANDIDATE_VALIDATED, never Firebird writes. Artifacts: docs/PRODUCT_KNOWLEDGE_BASE_V1.md, docs/AI_SEMANTIC_LAYER_V1.md, docs/AI_EVALUATION_V1.json, docs/AI_COST_PLAN_V1.json, docs/AI_RESIDUAL_PLAN_V1.json.

FISCAL_DATA_FOUNDATION_MOSTLY_COMPLETE. Deploy/pilot work remains paused per the current mission's scope. The operator manually placed the 6 official files that were blocked by the Portal NF-e/SPED session gate into sources/raw/bootstrap_v1/; all 6 were identified by content (never by filename), hashed, parsed, validated, and promoted ACTIVE — no internet access was attempted or needed this pass. 15 fiscal domains are now ACTIVE: NCM, TIPI_IPI, CEST, BA_ST_MVA, ORIGEM_MERCADORIA, CST_ICMS, CSOSN, CST_PIS, CST_COFINS (from the prior pass), plus CFOP, FCP, ANP, NCM_UTRIB, RTC_BIOFUEL_INDEX, RTC_CCREDPRES (new this pass) — 42,593 fiscal records persisted total. 11 domains remain MISSING_BOOTSTRAP_FILE (ICMS monofásico, RTC cClassTrib, RTC alíquotas CBS, and all 8 PIS/COFINS Tabela 4.3.10–4.3.17 files) — no file for them exists locally; internet access was deliberately not attempted this session. See docs/SOURCE_REGISTRY_V2.md. Read-only coverage over all 521 TESTESOFICIAIS.FDB products is in docs/FISCAL_COVERAGE_FULL_V2.json (supersedes _V1.json, kept for history). Zero Firebird writes this session (confirmed independently: 000026.IPI still 3.25, 109 non-null IPI rows unchanged).

PILOT_DEPLOYMENT_READY (previous mission's state, unchanged this pass). The Hub (central/) + Agent (agent/) system is operational end-to-end via a real Job queue, deploy-ready via Docker Compose, and the Agent is installable as a Windows Service. Formal Agent registration exists (central/scripts/register_agent.py), each Agent has its own token, and write authorization for a real client installation now requires two explicit, independently-configured layers (path + schema fingerprint) rather than the dev-only hardcoded path alone. See docs/OPERATIONS.md for day-to-day commands and docs/PILOT_DEPLOYMENT.md for standing up a new environment end to end.

Firebird discovery (Mission 00/01) remains read-only and unchanged; its sanitized evidence lives in docs/SAMPLE_PRODUCTS_V1.json and docs/MISSION_01_REPORT.md.

Architecture

  • Hub (central/app): FastAPI + PostgreSQL. Classifies NCM/CEST/TIPI/ BA_ST_MVA (classification.py), owns the Agent registry and Job queue (models.py, jobs.py, api.py). Never touches Firebird, never stores Firebird credentials.
  • Agent (agent/src/gti_fiscal_agent, agent/scripts): reads Firebird read-only, calls the Hub's /v1/classify/batch, runs the IPI auto-apply decision (ipi_auto_apply.py) through the shared pipeline (pipeline.py), and — only when FIREBIRD_WRITES=1 and the database path is exactly the authorized disposable test file — writes via the guarded executor (scripts/firebird_write_executor.py). Long-running entrypoint: agent/scripts/run_agent_service.py (heartbeat + Job poll loop). One-shot CLI variant kept for manual use: agent/scripts/run_agent_auto_apply.py.
  • Local per-product audit: Agent's SQLite (agent/agent_state.sqlite, table ipi_write_log). Central per-Job audit: Postgres jobs.result_summary.

Firebird write policy

TESTESOFICIAIS.FDB is the only Firebird database ever authorized for writes in development (see CLAUDE.md). For a real pilot installation, two independent, explicitly-configured layers are both required before any write connection opens — never inferred from a filename or .FDB extension: AUTHORIZED_WRITE_DATABASE_PATH (must equal that installation's own FIREBIRD_DATABASE_PATH) and EXPECTED_SCHEMA_FINGERPRINT (must equal the live schema fingerprint that installation reports). Leaving either unset falls back to the dev-only constant, so a real client database is refused by default (scripts/firebird_write_executor.py::assert_authorized_write_path / assert_schema_fingerprint_authorized, wired into agent/scripts/run_agent_service.py::execute_job). All other Firebird databases (including Banco/GTI.FDB, found but never opened) remain read-only. No real client database has been written to in any mission — every write so far targeted TESTESOFICIAIS.FDB.

Auto-apply scope

Only C000025.IPI, only when the Hub's TIPI match is rate_kind=NUMERIC and unambiguous. Never CEST, EX_TIPI, CFOP, CST/CSOSN, PIS/COFINS, MVA, or non-numeric/blank/legacy-NCM cases — those remain measurement-only (docs/DRY_RUN_PLAN_V4.json, docs/FULL_DATABASE_DRY_RUN_V1.json).

E2E proof (2026-09-18)

Real Job created via POST /v1/jobs (REPROCESS_PRODUCTS), claimed and executed by run_agent_service.py (not the manual batch script): manually-induced drift (C000025.IPI forced to NULL on product 000026) was detected and auto-restored to 3.25, verified independently in Firebird, jobs.result_summary on the Hub, and the Agent's local ipi_write_log. A second run against the same now-correct product produced writes_attempted=0 (idempotent no-op). REPROCESS_ALL over all 521 products completed with writes_attempted=0 (everything already correct from the prior full-database batch). A job created with FIREBIRD_WRITES=0 against a genuinely eligible drifted product completed with writes_attempted=0 and the ERP value left untouched, with the would-be write logged locally. The database was left in its correct state.

During this session Docker Desktop itself restarted mid-mission (external event, not induced); the Hub came back healthy on its own once Docker recovered, and all prior Job history was intact (Postgres volume persisted) — an incidental real-world confirmation of "Hub temporarily unavailable, Agent/data survive."

Pilot readiness proof (2026-09-18)

Registered a real Agent via central/scripts/register_agent.py (found and fixed a DetachedInstanceError bug in the process: the script read ORM attributes after the session closed — fixed to capture plain values before closing, with a regression test that mirrors the real CLI's usage pattern). Ran the Agent under that fresh, individually-hashed token end to end: startup validation, heartbeat (visible via GET /v1/agents/me, correctly derives ONLINE), job creation → claim → COMPLETED with a real product, restart with SQLite/central history intact, and a live path-guard test (fake client path, FIREBIRD_WRITES=1, no explicit authorization) that failed closed before any Firebird connection attempt and reported the job FAILED with a sanitized error while the Agent kept running. Also restarted the Hub and Postgres containers independently mid-session and confirmed the Agent/Hub recovered and history persisted through both.

Also fixed a Docker Compose bug introduced while making the Hub deploy-configurable: passing DATABASE_URL through from the shared root .env leaked the host-side (localhost-pointing) value into the container's network namespace, where localhost means the container itself, breaking Postgres connectivity. Removed that passthrough; the Hub's own postgres:5432 default (from central/app/config.py) is used instead.

Fiscal data foundation (2026-09-18)

docs/SOURCE_REGISTRY_V2.md is now the current source registry (V1 kept for history). Summary:

  • Preserved, re-verified: NCM (15,156 entries, hash-verified as unchanged — no internet available to re-fetch, so this is "verified against local record" not "re-fetched"), TIPI_IPI (11,103 entries, hash-verified byte-identical against the local xlsx), CEST (1,051 entries, preserved — no internet to re-fetch, no local raw copy to hash-check against).
  • Re-imported with updated provenance: BA_ST_MVA — the local PDF's bytes changed (new raw_sha256) but the parsed content is identical (303/303 records match exactly what was previously ACTIVE); promoted a new dataset row pointing at the current file rather than leaving provenance referencing a stale path.
  • New this session: fiscal_code_entries table (migration 0007) holding ORIGEM_MERCADORIA (9), CST_ICMS (11), CSOSN (10), CST_PIS (33), CST_COFINS (33) — extracted directly from the official moc7-anexo-i-leiaute-e-rv.pdf (Receita Federal/ENCAT), one shared Dataset row (kind=NFE_MOC_TABLES). Parser: central/app/datasets/nfe_moc_tables.py. Nothing invented: entries built from a combined official phrase (CST ICMS 40/41/50; CSOSN 102/103/300/400) keep that phrasing rather than being split further than the source itself supports; the 6 CST_COFINS codes not separately captured from the PDF's COFINS section (a page-boundary text-extraction artifact, not a semantic gap) are filled from the CST_PIS wording after confirming character-for-character identical wording for every code both extractors did capture — each such row is labeled metadata_json.mirrored_from = "CST_PIS".
  • Second pass (bootstrap_v1 operator-supplied files, migrations 0008/0009): the operator placed 6 previously-blocked official files directly into sources/raw/bootstrap_v1/. Each was identified by actual sheet names/header rows (never by filename) and promoted ACTIVE: CFOP (619 codes, Informe Técnico 2023.002, new dedicated cfop_entries table with all 14 official columns including the RTC-era indExcIBSCBS), FCP (27 UFs, fcp_rates), ANP (1031 cProdANP codes — confirmed the official NF-e portal table genuinely has no description column, not an omission — anp_product_codes), NCM_UTRIB (10,516 rows, ncm_utrib_entries), RTC_BIOFUEL_INDEX (23 rows, current + historical pBioObrigatorio, rtc_biofuel_index_entries), RTC_CCREDPRES (13 codes, all 18 official LC 214/2025 fields preserved — legal basis, CBS/IBS indicators, rates, vigências — rtc_cred_pres_entries). Bug found mid-import: cclass_nota_referenciada was VARCHAR(16) but some official rows carry free text there; widened to Text in migration 0009 after a clean rollback (no orphan row, no data loss). New read-only endpoints: GET /v1/reference/{cfop,ncm_utrib,anp,fcp}/{code}.
  • Still MISSING_BOOTSTRAP_FILE (not FAILED — simply no file provided, internet deliberately not attempted this session): ICMS monofásico (combustíveis), RTC cClassTrib, RTC alíquotas CBS, and all 8 PIS/COFINS Tabela 4.3.10–4.3.17 files.
  • Coverage matrix: docs/FISCAL_COVERAGE_FULL_V2.json (supersedes _V1.json), all 521 products in TESTESOFICIAIS.FDB, read-only (zero Firebird writes). NCM 495 valid / 3 legacy / 23 invalid / 0 missing. IPI 107 numeric / 20 non-numeric / 297 source-blank / 71 ambiguous / 23 no-match. CEST: 462 matches, 0 confirmed (structural ambiguity, unchanged), 15 candidates. BA_ST_MVA: 268 matches, 31 resolved, 237 candidates. New: CFOP match_found=498/521 (this product's own C000025.CFOP value is an officially valid code); NCM_UTRIB match_found=495/521 (tracks ncm_valid exactly, as expected). ANP/FCP/ RTC_CCREDPRES/RTC_BIOFUEL_INDEX are DATA_AVAILABLE=true but match_found=0 — stated explicitly as a data-shape fact (this grocery/ restaurant ERP has no ANP/fuel-product/UF join key on C000025), not a gap or a bug. PIS/COFINS CST domain remains DATA_AVAILABLE, RULE_RESOLVED=0 for all products (same reason as before: no NCM-linked determination table exists to acquire yet).

Safety counters (cumulative across missions)

  • APPLICATION_DDL_DML_WRITES against Firebird: only ever against TESTESOFICIAIS.FDB, only UPDATE C000025.IPI / UPDATE C000025.CEST-class statements via the guarded executor (CEST never actually written — no CEST_CONFIRMED case existed with enough evidence).
  • OTHER_DATABASES_ACCESSED = 0 (including Banco/GTI.FDB, never opened).
  • Postgres migrations: 0001..0009 applied (0006 adds jobs table and agents status/heartbeat columns; 0007 adds fiscal_code_entries; 0008 adds cfop_entries/fcp_rates/anp_product_codes/ ncm_utrib_entries/rtc_biofuel_index_entries/rtc_cred_pres_entries; 0009 widens rtc_cred_pres_entries.cclass_nota_referenciada to Text).
  • This session: FIREBIRD_WRITES=0 throughout — zero writes, zero DML/DDL against any Firebird database (only read-only SELECTs to build the coverage matrix); independently re-verified after the session (000026.IPI still 3.25, 109 non-null IPI rows unchanged).
  • Total fiscal records persisted in Postgres across all domains: 42,593.

Threat model (Firebird discovery boundary, unchanged since Mission 00)

The structured boundary protects normal callers against implementation mistakes, accidental unauthorized operations, arbitrary SQL exposed to normal callers, the wrong database, writes, procedures/UDFs, injection, and dependency shadowing. TRUSTED_PROCESS = TRUE: arbitrary Python already running in the same process, including deliberate introspection or monkey patching, is outside scope. No artificial cryptographic controls are added.

Known gaps / next steps

  • No "list all agents" or "disable agent" HTTP endpoint yet — disabling an agent is a direct agents.enabled update via DATABASE_URL access (see docs/PILOT_DEPLOYMENT.md's "Rollback"). Fine for pilot scale; a small admin surface is a natural follow-up once there's more than a couple of installations.
  • The Windows Service wrapper (agent/service/gti_agent_windows_service.py) was written and unit-verified (module imports cleanly, stop-flag mechanism tested against run_agent_service.run()), but was not registered with the Windows Service Control Manager on this machine — sc.exe/pywin32 install modifies system-wide service state, which this session treats as outside what it does unprompted. An operator runs the documented install/start commands themselves.
  • No auto-update mechanism (deliberately out of scope this round) — updates are a manual stop/replace/start, documented in docs/PILOT_DEPLOYMENT.md.
  • CEST matching still cannot confirm any of the 5 known-ambiguous products (structural NCM→multiple-CEST ambiguity in CONFAZ data, not a matcher bug — see docs/DRY_RUN_PLAN_V4.json).
  • NT_STATUS_FIELD (how a TIPI "NT" marker should map to C000025.CST_IPI) remains unmapped — no reliable ERP evidence found for it.
  • 11 fiscal domains are MISSING_BOOTSTRAP_FILE (not FAILED — no file was ever provided for them): ICMS monofásico, RTC cClassTrib, RTC alíquotas CBS, and all 8 PIS/COFINS Tabela 4.3.10–4.3.17 files. CFOP, FCP, ANP, NCM×uTrib, RTC_CCREDPRES, RTC_BIOFUEL_INDEX — all previously FAILED — are now ACTIVE (operator supplied the official files). See docs/SOURCE_REGISTRY_V2.md for exact evidence and next steps.
  • PIS/COFINS: only the CST code domain (Tabela 4.3.3/4.3.4-equivalent) is ACTIVE. The NCM-linked determination tables (SPED Guia Prático EFD-Contribuições Tabelas 4.3.10–4.3.17: alíquotas diferenciadas, monofásicas, ST, zero, isenção, sem incidência, suspensão) were not acquired — no file provided, internet deliberately not attempted.
  • CST_ICMS/CSOSN/ORIGEM_MERCADORIA/CST_PIS/CST_COFINS are consultable (DatasetService.fiscal_codes(domain, code=None)) but not yet wired into Classifier — no per-product automatic determination exists for these domains, by design (mission explicitly forbade inventing fiscal rules).

Git State

No Git identity was configured and no Git configuration was modified this session.

Business scope (2026-09-19)

Clients: restaurants, markets, clothing/retail, workshops, commerce. No fuel stations, no EFD-Contribuicoes. CORE_MVP still missing only RTC_CCLASSTRIB and RTC_CBS_RATES (MISSING_BOOTSTRAP_FILE, awaiting operator files; see SOURCE_REGISTRY_V2.md). ICMS_MONOPHASIC = OUT_OF_SCOPE_MVP; ANP and RTC_BIOFUEL_INDEX = ACTIVE_REFERENCE_ONLY; PIS_COFINS_4_3_10..17 = DEFERRED_BY_BUSINESS_SCOPE (not deploy blockers; existing support kept). FISCAL_COVERAGE_FULL_V3 to be generated once the two core domains are ACTIVE.

CORE MVP fiscal foundation COMPLETE (2026-09-19)

RTC_CCLASSTRIB (164 cClassTrib + 18 CST) and RTC_CBS_RATES (2 rows) are ACTIVE (migration 0010, endpoints /v1/reference/rtc_cclasstrib/{code}, /v1/reference/rtc_cbs_rate/{year}). All 14 CORE domains ACTIVE; nothing CORE is missing. Total fiscal records 42,777. Coverage: docs/FISCAL_COVERAGE_FULL_V3.json (V2 kept for history). cClassTrib/cCredPres have no ERP join key → DATA_AVAILABLE only; no rule invented. Zero Firebird writes.

Fiscal Decision Engine V1 (2026-09-19)

central/app/fiscal_decision.py (pure, DECISION_ENGINE_VERSION=fiscal-decision-v1, carried in every provenance) + scripts/run_fiscal_decision_v1.py (read-only over the 521 products; --apply writes only AUTO_APPLY_SAFE via the guarded executor). Artifacts: docs/FISCAL_DECISION_MATRIX_V1.json (per product/domain: current/proposed/status/reason/confidence/provenance/write_eligible + ERP field map), docs/FISCAL_DECISION_COVERAGE_V1.json, docs/NEXT_FISCAL_AUTOMATION_PRIORITIES_V1.md. Company context read live: UF=BA, CRT=1 (C000004). ERP mappings: 11 confirmed, 10 MAPPING_UNCONFIRMED (CFOP_ENTRADA, CST_ENTRADA, COD_*_PIS/COFINS, ALIQ_PIS/COFINS, CST_IPI, ORIGEM). Result: AUTO_APPLY_SAFE = 0 (IPI already correct on all 107 numeric rows; CEST never single-confirmed; a lone EX row per NCM is NOT evidence for a product — needs description match). CFOP/CSOSN/CST/PIS/COFINS/FCP/RTC = OPERATION_CONTEXT_REQUIRED or unresolved; CBS 0.009 kept as reference only, never applied universally. No Hub classify change, so no classification_cache bump needed. Helper scripts/audit_erp_fields.py (read-only). New endpoint GET /v1/reference/fiscal_codes/{domain}. Zero Firebird writes this mission.

ERP Fiscal Context Discovery V1 (2026-09-19, read-only)

Artifacts: docs/ERP_FISCAL_FIELD_MAP_V1.json, FISCAL_CONTEXT_REQUIREMENTS_V1.json, FISCAL_CONTEXT_COVERAGE_V1.json, NEXT_FISCAL_FIELD_IMPLEMENTATION_V1.md; raw evidence in docs/runs/erp_*. Scripts (all read-only): discover_erp_schema.py, inspect_erp_table.py, analyze_erp_fiscal_history*.py, generate_erp_context_docs.py. Key facts: NFCE_ITENS (34,946 emitted NFC-e items 2018-2026) + NFCE (1,502) are real fiscal history; C000030 = 590 naturezas (CFOP/TIPO 1 entrada,2 saída); C000001.IND_CFOP_* company defaults; C000004: UF=BA, CRT=1, OPTANTE_SIMPLES=S, ICMS_CSOSN=102. Vendor DDL/scripts under D:/PROJETOS/GTISISTEMAS/Diretorios (C000083 confirms CST = origem+CST 3 digits). C000025 CFOP/CST/CSOSN/CEST are product defaults (99%/100%/95%/99% equal to emitted on identity-consistent joins). CFOP_ENTRADA/CST_ENTRADA/ORIGEM/PIS/COFINS product fields are unused/legacy. MVA: NO_PRODUCT_FIELD proven (MARGEM_AGREGADA 0/521, unused in C000062). WARNING: NFC-e items show product identity drift (57% item NCM != current product NCM for same CODPRODUTO) — validate history only on consistent joins. GTI.FDB (Banco) was NOT opened. Zero Firebird writes.

Product Fiscal Defaults Engine V1 (2026-09-19)

central/app/product_defaults.py (product-defaults-v1, pure) + scripts/run_product_defaults_v1.py, reconcile_ncm_history.py, profile_erp_operations.py. Findings: of 1,676 NFC-e NCM mismatches 1,661 are BAD_JOIN (97% of mismatches sit in 2022-12; CODPRODUTO does not identify the product there; 1,751 items have no product at all), 4 TRUE_HISTORICAL_DIFFERENCE, 11 UNKNOWN, 0 technical bugs. Trusted corpus = 1,146 identity-proven items. All 1,499 keyed NFC-e are model 65 / UF 29 (BA): saída, intra-UF, consumidor final. CFOP: 222 consistent with ST status (already correct), 29 ST conflicts (review; 15 of them the ERP actually emitted the current CFOP), 244 context required, 26 unresolved. CSOSN: never resolved (no ICMS-treatment or ST-retention source); blank CSOSN does NOT default to company 102 in emission (history emitted 101). CST = reference only (CRT=1). AUTO_APPLY_SAFE=0, writes 0, no auto-repair proof (no safe rule). Artifacts: PRODUCT_FISCAL_DEFAULTS_{MATRIX,DRY_RUN}_V1.json, NCM_HISTORICAL_RECONCILIATION_V1.json, ERP_OPERATION_PROFILES_V1.json, FISCAL_CONTEXT_REQUIREMENTS_V2.json, NEXT_FISCAL_FIELD_IMPLEMENTATION_V2.md. No Hub classify change => no cache bump.

ICMS Context Engine V1 (2026-09-19, read-only)

central/app/icms_context.py (icms-context-v1, pure) + scripts/run_icms_context_v1.py. Purchase-side evidence is thin: only C000088 (12 items, ICMS_RETIDO='N', ST values 0 → 11 products WITHHELD_NOT_CONFIRMED, 0 CONFIRMED). Role evidence: C000025.TIPO (7 MATERIA-PRIMA=INPUT), PROD_COZINHA/RECEITA_INGREDIENTES/C000032.TIPO=COMPOSICAO (own production), purchase docs (resale); TIPO_ITEM/PRODUTO_SERVICO unused. ST: 0 confirmed (ERP CEST blank for 490/521 so no corroboration), 28 not applicable, 268 candidates, 225 unknown. CSOSN V2: 0 resolved, 5 candidates, 490 context required, 26 unresolved (NCM); nothing auto-appliable. ST conflict audit (29): ERP_HISTORY_CONFLICT 14, DESCRIPTION_MISMATCH 8, MULTIPLE_CEST 4, BA_ST_RULE_CONFLICT 1, NO_EVIDENCE 2. Artifacts: ICMS_PRODUCT_CONTEXT_V1, ICMS_CONTEXT_COVERAGE_V1, CSOSN_DRY_RUN_V2, FISCAL_CONTEXT_REQUIREMENTS_V3, NEXT_FISCAL_FIELD_IMPLEMENTATION_V3. No Hub change => no cache bump. Zero Firebird writes.

SGV product-screen mapping (2026-09-19)

Architecture rule (also in CLAUDE.md): GTI Fiscal writes ONLY the canonical product record C000025; transactional tables are evidence only (executor allowlist is C000025-only; tests enforce refusal for NFCE/NFCE_ITENS/C000032/C000061/C000062/C000088/INVENTARIO). Map: docs/SGV_PRODUCT_FISCAL_UI_MAP_V1.json (25 UI fields; 0 confirmed by UI diff yet; write_target=true for 6 by equivalent strong evidence: CLASSIFICACAO_FISCAL, CFOP, CST, CEST, IPI, EX_TIPI). UI oracle tooling: scripts/sgv_product_snapshot.py before|diff (read-only, ledger in docs/runs/sgv_ui_diff_ledger.json) + operator sheet docs/SGV_UI_MAPPING_SESSION_V1.md; regenerate the map with scripts/generate_sgv_ui_map.py after diffs. Company UF/CRT now read via gti_fiscal_agent.firebird_reader.read_company_context (fail-closed; runners no longer hardcode BA). Multi-DB read-only auditor: python audit_firebird_database.py <FDB>.

SGV UI mapping session tool (2026-09-19)

python scripts/map_product_ui_fields.py --product 001434 — interactive, read-only oracle: operator changes one field at a time in the SGV, tool diffs the full C000025 row, classifies DIRECT_BINDING / DERIVED/COUPLED_FIELD / AUTOMATIC_METADATA / AMBIGUOUS (never picks among several changed columns), persists docs/SGV_PRODUCT_FISCAL_UI_DIFF_SESSION_V1.json step by step (resumable, --restart), prints the original values to restore at the end. python scripts/generate_sgv_ui_map.py then rebuilds docs/SGV_PRODUCT_FISCAL_UI_MAP_V1.json (confirmed_by_ui_diff only for DIRECT_BINDING; a control bound to an unexpected column replaces the hypothesised one). Session pending.

SGV product screen mapped from the executable's DFM (2026-09-19)

scripts/extract_sgv_dfm_bindings.py <SGV.exe> parses the Delphi binary DFM of Tfrmproduto (read-only): every fiscal control on the Config. Impostos / Estoque tabs is a data-aware control on dsproduto2 -> qrproduto (select * from c000025) with an explicit DataField; label/control positions match the screenshots. Result in docs/SGV_PRODUCT_FISCAL_UI_MAP_V1.json: 23 UI controls, 21 CONFIRMED_STRONG, 1 PROBABLE (origem: RadioGroup + hidden edit bound to COD_ORIGEM_MERCADORIA), 1 NO_MATCH (CSOSN description combo is not data-aware). Screen writes CFOP_ENTRADA/CST_ENTRADA/ORIGEM-code even though they are empty in the data. The T/I/N/S selector next to CEST is SITUACAO_TRIBUTARIA (S = Substituição Tributária = the ERP's own product ST flag; 0/521 products use S). Cálculo Preço tab controls (IPI/PIS/COFINS) bind to C000026 (price calc), not C000025. write_target = binding proven; the executor allowlist and the fiscal rule engines still decide what may actually be written (executor: C000025.IPI only).

Multi-database validation (2026-09-19, read-only, Banco/*.FDB git-ignored)

4 copies audited: CONSTRUCAO (construction, 3,712 products, BA/CRT1), MMMODAS (apparel, 20,997, BA/CRT2), RINCAO (restaurant, 894, C000004 has 3 company rows -> ambiguous), UNIAO (market, 8,942, BA/CRT3). All expose C000004/C000025 with the 21 write-target columns and no type variation except UNIAO CEST VARCHAR(10) vs (7). One generic Agent is viable; needs company selector + CRT source of truth (C000004.CRT disagrees with EMISSOR.EMITENTE_CRT in 3 of 4) and capability detection (scripts/sgv_capabilities.py). Tools: scripts/audit_multi_databases.py, run_multi_db_coverage.py (in-container bulk classifier bulk_classify_in_container.py), generate_multi_db_findings.py. Artifacts: MULTI_DATABASE_{SCHEMA_AUDIT,FISCAL_COVERAGE,FIELD_COMPATIBILITY}_V1.json, MULTI_DATABASE_FINDINGS_V1.md. No auto-repair DB provided. Zero writes.

Company context resolution + cache fix (2026-09-19)

agent/src/gti_fiscal_agent/company_context.py: deterministic ACTIVE_COMPANY/UF/CRT resolver (EMISSOR CNPJ ↔ C000004 CNPJ ↔ emitted NFC-e key CNPJ; CRT authority for emission = EMISSOR because the emitter executables bind QrEmissor.EMITENTE_*; C000004.CRT is the ERP/SPED filial registry). Never uses first row; config COMPANY_CODE/COMPANY_CNPJ/COMPANY_CRT_SOURCE; preflight agent/scripts/preflight_company_context.py, enforced at Agent startup; read_company_context fails closed. 4 databases: CONSTRUCAO AUTO_RESOLVED; MMMODAS/RINCAO/UNIAO CRT_SOURCE_CONFLICT (need COMPANY_CRT_SOURCE). Rules: docs/AGENT_COMPANY_CONTEXT_RULES_V1.md; evidence: COMPANY_CONTEXT_RESOLUTION_V1.json, MULTI_DATABASE_COMPANY_CONTEXT_V1.json. Hub classification_cache fixed: fingerprint (tenant + input + ENGINE_VERSION + dataset_versions()) is computed first and a HIT returns without running the Classifier (test: central/tests/test_classification_cache.py). Live benchmark (100 products): MISS 337 ms/product, HIT 15 ms/product, 100/100 hits, identical results. Fingerprint inputs unchanged, so existing cache entries stay valid and version bumps still invalidate naturally.

Release Candidate 1.0.0-rc1 (2026-09-19) — COMPLETE_LOCAL_RC

Frozen: release/VERSION, central/app/version.py (GET /version), release/RELEASE_MANIFEST.json (versions + ACTIVE dataset hashes + migrations; head 0022_agent_releases_enrollment since 2026-09-23). See docs/RELEASE_CANDIDATE_V1.md. Architecture: Hub central (Docker Hub+PostgreSQL, deploy/hub) <-HTTPS- Agent Windows (deploy/agent-windows, service GTIFiscalAgent) -local read-only-> SGV Firebird. The Hub never accesses Firebird. Agent: FISCAL_MODE DRY_RUN|AUTO_APPLY (DRY_RUN = technically no writes), COMPANY_CODE/COMPANY_CNPJ/COMPANY_CRT_SOURCE(AUTO|EMISSOR|C000004), COMPANY_UF explicit override only; single preflight agent/scripts/preflight.py (also enforced at service start); REPROCESS_ALL job result includes the coverage report + company context (gti_fiscal_agent/coverage.py, shared with multi-db tooling); scripts/prove_zero_write.py (DML counters + table digests). Packaging: scripts/build_agent_package.py (secret-scanned zip). Hub prod: deploy/hub/docker-compose.prod.yml (Postgres internal only), backup/restore, dataset seed export/import; APP_ENV=production refuses AGENT_DEV_TOKEN. Pilot profile: CONSTRUCAO (pilot-construcao.env.example); planned, not installed: MMMODAS/RINCAO/UNIAO with COMPANY_CRT_SOURCE=EMISSOR. No real host was accessed: a local simulation ran the Agent (registered agent, DRY_RUN) end-to-end on the CONSTRUCAO copy. Follow-up: memoize dataset_versions().

Local Completion V1 (2026-09)

  • Contrato: docs/FISCAL_FIELD_CONTRACT_V1.json (23 campos, gerado por scripts/generate_field_contract.py); resolvedor puro central/app/field_contract.py.
  • Contexto do operador (fatos que o ERP não conhece): tabela fiscal_context_entries (migration 0011), POST /v1/context/import, scripts/import_fiscal_context.py (CSV/JSON). Hierarquia PRODUCT > GROUP > NCM > COMPANY.
  • Escrita: somente as 5 cópias descartáveis em docs/LOCAL_WRITE_ALLOWLIST_V1.json (caminho exato), só C000025, via execute_guarded_product_update.
  • Runner: scripts/local_completion.py (estado em docs/runs/local_completion/, git-ignored); relatórios: scripts/local_completion_report.py.
  • Resultado: 41.354 células corrigidas, 100% verificadas, 2ª passada 0 escritas; 255.643 células aguardam resposta (docs/LOCAL_FISCAL_CONTEXT_QUESTIONS_V1.*, 1.054 perguntas). Fontes externas pendentes: docs/LOCAL_COMPLETION_MANUAL_FILES_V1.md.

Local Completion V2 (2026-09-20)

Audit outcome in docs/LOCAL_COMPLETION_V2_REPORT.md: origin rule invalid → rolled back (35,048); CFOP/IPPT weak-evidence writes rolled back; role no longer inferred from PROD_COZINHA alone; SPED 4.3.x dataset in docs/sources/; PIS/COFINS Simples rule from ERP emission evidence; questionnaire V2 (docs/LOCAL_FISCAL_CONTEXT_QUESTIONS_V2.*). Rollback tool: scripts/rollback_field.py; audit: scripts/audit_local_writes.py.

Local Completion V3 Operations

Tasks 8-10 are CLI-first and review-first: company/group answers are validated locally and emitted as JSON/CSV without import or Firebird writes; MIXED_OR_DYNAMIC never invents a static entry CFOP; STORE_IPI_PROFILE requires an explicit choice and produces only a rollback plan. Rollback previews are scoped by run, database, field, and product and refuse drift where current differs from the audited after value. scripts/run_local_validation.py and scripts/generate_local_health.py are deterministic and emit docs/LOCAL_PRODUCT_HEALTH_V1.json with measured counts.

PRE-AI Local Completion V3 (2026-09-21)

Offline completion is operationally ready. DeterministicFakeSemanticProvider and the ID-correlated batch runner cover batch sizes 5/10/20 plus contract and transport failures; budget, cache-key, dataset-drift, and zero-write E2E tests are present. KB conflict audit is conservative: 90 observable GTIN/NCM conflicts remain NCM_CONFLICT; 73 historical conflicts are explicitly uncovered by the seed payload. Canonical identity is deterministic and excludes tenant fiscal context. Retrieval V3 evidence hooks exist but V2 remains active until a full untouched holdout replay proves no regression. Human/group questionnaire CLIs, guarded rollback preview, local validation, and health report are available. See docs/PRE_AI_LOCAL_COMPLETION_V3_REPORT.md for measured values.

Production Hub + migration baseline (2026-09-23)

  • Hub + PostgreSQL run on VM 10.0.10.117 (deploy/hub, compose project gti-fiscal-hub), next to the GTI Gerenciador but isolated (own DB, process, login). Public HTTPS gtifiscal.gtigrupo.com.br via the GTI Nginx Proxy Manager (10.0.10.100) -> http://10.0.10.117:8000 (DOCKER-USER allows only the NPM).
  • Human access: SSO from GTI Gerenciador (Ed25519 assertion, nonce, single-use jti; collaborators in admin_users with auth_source=GTI_GERENCIADOR, no password); local admin only as break-glass at /login/local. Same visual identity as the panel (static/gti-theme.css is a copy of its design system).
  • Migration head: 0022_agent_releases_enrollment. An empty database is initialized by alembic upgrade head alone — never stamp/create_all.
  • 0001 is a frozen explicit baseline (it used to call create_all(), which broke every fresh install). 0019 fixes the frozen created_at defaults from 0006; 0020 enforces NOT NULL on 20 defaulted timestamps.
  • Verified: fresh DB == upgraded real-history DB == production (pg_dump); vs models, 0 differences on nullability/types/PK/FK/uniqueness/checks. Remaining drift is naming, column order, extra DB defaults, redundant indexes.
  • deploy/hub/*.sh are forced to LF (.gitattributes); backup.sh + restore into a scratch database were exercised on production.