fix: gate cClassTrib applicability by CRT and date

This commit is contained in:
gti
2026-09-25 17:47:04 -03:00
parent 95605c2ff5
commit 4a12640031
25 changed files with 498 additions and 72 deletions
@@ -0,0 +1,208 @@
# cClassTrib Applicability Implementation Plan
> **For agentic workers:** Execute this plan inline task-by-task with test checkpoints. Do not delegate or publish an Agent without following the release gate in Task 5.
**Goal:** Gate cClassTrib validation, blockers, and completeness by live company CRT and its official effective date, instead of treating global RTC enablement as universal requiredness.
**Architecture:** Keep `PARAMETROS.USAR_REFORMA_TRIBUTARIA` as the ERP-level RTC switch, then calculate a separate company-level cClassTrib applicability state from the already-read `C000004.CRT` and the current date. The shared pure evaluator owns the date/regime rule; the Agent carries the state into per-product findings and fingerprints; existing Hub presentation surfaces show a concise non-applicable label without adding another coverage panel.
**Tech Stack:** Python, pytest, SQLAlchemy-backed Hub presentation, Jinja templates, existing read-only Firebird Agent connection.
## Global Constraints
- Do not change architecture or add a new SGV flag, schema, API, or second coverage panel.
- Do not change NCM, PIS, COFINS, or other fiscal rules.
- Do not write to Firebird; reads use the existing `READ COMMITTED READ ONLY` Agent connection.
- Preserve Agent 2's current `rtc_write_mode=DRY_RUN`; `REAL_WRITES=0` and `CCLASSTRIB_WRITES=0`.
- `PARAMETROS.USAR_REFORMA_TRIBUTARIA` remains the RTC feature switch, not proof that every product requires cClassTrib.
- cClassTrib applicability uses live company CRT: CRT 3 from 2026-08-03; CRT 1/2/4 from 2027-01-01; unresolved/unsupported CRT is not applicable until resolved.
- Agent 2 baseline: total 521, complete 0, safe-blocked 521, failed 0, unclassified 0, coverage 0%; RTC 521 blockers; PIS 1, COFINS 1, NCM 26 distinct products/29 open rows.
- Current code uses Agent-side assessment; do not claim the live Agent 2 result changed until the updated Agent code is running and a new job completes.
---
### Task 1: Add failing pure applicability tests
**Files:**
- Modify: `central/tests/test_unified_fiscal_coverage.py`
- Test: `central/app/unified_fiscal_coverage.py`
**Interfaces:**
- Existing input is `evaluate_product(product, company, rtc, today)`; use `company["crt"]`, `rtc["enabled"]`, and the supplied date.
- Applicability states remain in `result["applicability"]["RTC"]`; basis is exposed in `result["basis"]["RTC"]`.
- [ ] **Step 1: Add the business-rule tests**
Add tests equivalent to:
```python
def test_cclasstrib_is_not_applicable_for_simples_before_2027_even_when_rtc_is_on():
result = evaluate_product({**EMPTY, "CCLASSTRIB": None}, SIMPLES, ON, date(2026, 9, 25))
assert result["applicability"]["RTC"] == "NOT_APPLICABLE"
assert "CCLASSTRIB" not in result["mandatory_fields"]
assert not [b for b in result["blockers"] if b["category"] == "RTC"]
def test_cclasstrib_effective_date_is_inclusive_by_crt():
for crt in ("1", "2", "4"):
before = evaluate_product({**EMPTY, "CCLASSTRIB": None}, {"crt": crt}, ON, date(2026, 12, 31))
on_date = evaluate_product({**EMPTY, "CCLASSTRIB": None}, {"crt": crt}, ON, date(2027, 1, 1))
assert before["applicability"]["RTC"] == "NOT_APPLICABLE"
assert "CCLASSTRIB_MISSING" in kinds(on_date)
before = evaluate_product({**EMPTY, "CCLASSTRIB": None}, NORMAL, ON, date(2026, 8, 2))
on_date = evaluate_product({**EMPTY, "CCLASSTRIB": None}, NORMAL, ON, date(2026, 8, 3))
assert before["applicability"]["RTC"] == "NOT_APPLICABLE"
assert "CCLASSTRIB_MISSING" in kinds(on_date)
def test_non_applicable_cclasstrib_does_not_mask_pis_cofins_blockers():
product = {**EMPTY, "CCLASSTRIB": None, "PISCOFINS": "S", "INCIDENCIA_PISCOFINS": "MONOFÁSICA"}
result = evaluate_product(product, SIMPLES, ON, date(2026, 9, 25))
assert kinds(result) == {"PIS_CST_MISSING", "COFINS_CST_MISSING"}
```
Also cover RTC off, missing/unsupported CRT, and applicable CRT 3 with empty cClassTrib.
- [ ] **Step 2: Run the tests and confirm the current code fails**
Run: `python -m pytest central/tests/test_unified_fiscal_coverage.py -q`
Expected: new applicability assertions fail because RTC enabled currently makes the empty field required for CRT 1/2/4 and before the effective dates.
---
### Task 2: Implement one shared applicability gate and carry it through Agent findings
**Files:**
- Modify: `central/app/unified_fiscal_coverage.py`
- Modify: `agent/src/gti_fiscal_agent/unified_fiscal.py`
- Test: `central/tests/test_unified_fiscal_coverage.py`
- Test: `agent/tests/test_unified_fiscal.py`
**Interfaces:**
- Add a pure helper with the stable signature `cclasstrib_applicability(crt, rtc_enabled, today) -> dict` returning `state`, `basis`, and `effective_from` as an ISO-8601 string or `None` so the summary is JSON serializable.
- Pass the result through the existing `rtc` context to `evaluate_rtc`; do not add a Firebird setting or database write.
- Add the applicability inputs to each product input fingerprint so changes in company CRT/effective state invalidate stale decisions.
- Include the company-level applicability result and source (`C000004.CRT` plus effective-date rule) in the Agent's `fiscal_context` summary.
- [ ] **Step 1: Implement the helper and make `evaluate_rtc` return no cClassTrib blocker when the gate is false**
Use these effective dates and exact CRT sets:
```python
CCLASSTRIB_EFFECTIVE_FROM = {
"3": date(2026, 8, 3),
"1": date(2027, 1, 1),
"2": date(2027, 1, 1),
"4": date(2027, 1, 1),
}
```
RTC disabled, CRT absent/unsupported, and dates before the CRT effective date must return `NOT_APPLICABLE`. For an applicable company, retain the existing catalog validation and missing-value blocker.
- [ ] **Step 2: Update the Agent evaluation context and input fingerprint**
Calculate the company gate once per assessment using the live `company["crt"]` and `today`; pass it to each `evaluate_product`. Extend fingerprint input with the resulting applicability state (not a cached Hub value). Continue to issue only SELECT statements to Firebird.
- [ ] **Step 3: Extend the Agent fake-cursor regression tests**
Cover RTC param on + CRT 1 before 2027 (no RTC finding), RTC param on + CRT 3 on/after 2026-08-03 (empty value blocked), separate PIS/COFINS findings preserved, the observed applicability source/state in `fiscal_context`, and a SELECT-only statement list.
- [ ] **Step 4: Run pure and Agent evaluator tests**
Run: `python -m pytest central/tests/test_unified_fiscal_coverage.py agent/tests/test_unified_fiscal.py -q`
Expected: PASS; all cursor statements in Agent discovery tests are SELECT-only.
---
### Task 3: Prove no candidates or writes while cClassTrib is not applicable
**Files:**
- Modify: `agent/tests/test_rtc_auto_apply.py` only if an existing case does not cover the no-finding path.
- Test: `agent/src/gti_fiscal_agent/rtc_auto_apply.py` and the assessment output from Task 2.
**Interfaces:**
- Candidate creation continues to consume the existing RTC findings; a non-applicable product must have no RTC missing finding and therefore no R3 candidate.
- Auto-apply authorization and allowed field remain unchanged.
- [ ] **Step 1: Add a focused candidate/write regression if needed**
Use RTC enabled, CRT 1, empty `CCLASSTRIB`, and the existing fake read-only Firebird connection. Assert zero `CCLASSTRIB_MISSING*` findings, zero preview candidates, zero write attempts, zero write successes, and no SQL other than SELECT on the assessment path.
- [ ] **Step 2: Run RTC auto-apply tests**
Run: `python -m pytest agent/tests/test_rtc_auto_apply.py agent/tests/test_unified_fiscal.py -q`
Expected: PASS; existing applicable CRT 3 safe-write unit tests remain valid and the non-applicable case has no candidate/write.
---
### Task 4: Present non-applicability on existing Hub views
**Files:**
- Modify: `central/app/panel_presentation.py`
- Modify: `central/templates/rtc_control.html`
- Modify: `central/templates/agent_detail.html`
- Test: `central/tests/test_control_panel_pages.py`
- Test: `central/tests/test_operational_view_models.py`
**Interfaces:**
- Preserve the existing `ops.rtc.enabled` value for the global RTC status.
- Add a presentation state for cClassTrib applicability; when false, do not render the current “cClassTrib pendente em N produtos” summary or RTC blocker empty-state warning for that field.
- Render one concise label in the existing RTC status areas: `cClassTrib: Não utilizado neste cliente`.
- [ ] **Step 1: Add a presentation test using Agent 2-like data**
Supply `rtc_enabled=true`, CRT 1, 521 resolution rows, 521 prior cClassTrib blocker products, and the derived non-applicable state. Assert the displayed label and assert that “cClassTrib pendente em 521 produtos” is absent on the Agent detail and RTC pages. Keep the single existing fiscal coverage metrics unchanged.
- [ ] **Step 2: Implement the smallest existing-view changes**
Have `rtc_status_view` use the Agent-reported applicability state. Show the concise label in the existing summary areas and suppress cClassTrib-pending metrics when not applicable. Do not add a new panel or alter the current RTC write mode.
- [ ] **Step 3: Run presentation tests**
Run: `python -m pytest central/tests/test_control_panel_pages.py central/tests/test_operational_view_models.py -q`
Expected: PASS; applicable CRT 3 views continue showing the existing candidate/coverage information.
---
### Task 5: Run the full suite, then gate Agent release and live reprocess
**Files:**
- Version/release files only after the user confirms the release action: `release/VERSION`, `central/app/version.py`, `release/RELEASE_MANIFEST.json`, and the installer metadata generated by `scripts/build_windows_installer.ps1`.
- No Firebird tables or records are modified.
**Interfaces:**
- The updated Agent must be running for Agent 2's live `REPROCESS_ALL` to exercise the new gate; the current production Agent is version 1.2.4.
- Official Hub deployment entry point is `scripts/deploy_hub.ps1`; a new Agent release uses `-PublishAgent` and the installer build script.
- [x] **Step 1: Run all offline tests before any version bump**
Run: `python -m pytest tests -q`
Also run the relevant Agent and Hub tests together: `python -m pytest central/tests/test_unified_fiscal_coverage.py central/tests/test_control_panel_pages.py central/tests/test_operational_view_models.py central/tests/test_zero_touch_hub.py agent/tests/test_unified_fiscal.py agent/tests/test_rtc_auto_apply.py agent/tests/test_zero_touch_agent.py -q`
Expected: PASS with no Firebird writes.
- [x] **Step 2: Pause at the Agent release gate**
Reported that the rule change is in Agent-executed logic, listed the Agent files/tests, and stated the current deployed version `1.2.4`. The operator explicitly authorized bumping to `1.2.5`, building/publishing, and upgrading Agent 2 in place.
- [ ] **Step 3: After release approval, build and publish through official scripts**
Run the repository's supported installer build script, then:
```powershell
powershell -ExecutionPolicy Bypass -File .\scripts\deploy_hub.ps1 -PublishAgent -Version 1.2.5
```
Do not use ad-hoc SSH/Docker deployment. Confirm the deployed Hub health/version and that Agent 2 is running the new Agent version before queuing its full reprocess.
- [ ] **Step 4: Reprocess Agent 2 and compare read-only ERP results**
Queue `REPROCESS_ALL` only after the updated Agent is confirmed running. Keep `rtc_write_mode=DRY_RUN`. Compare the new snapshot/job against the recorded baseline for total, complete, safe-blocked, failed, unclassified, coverage, cClassTrib blockers, and distinct NCM/PIS/COFINS blockers. Confirm `PARAMETROS.USAR_REFORMA_TRIBUTARIA` was observed live, `C000004.CRT=1` led to cClassTrib non-applicability as of 2026-09-25, and Firebird `WRITE_ATTEMPTS=0`, `WRITE_SUCCESSES=0`, `CCLASSTRIB_WRITES=0`.
Expected for this CRT 1 client before 2027-01-01: RTC cClassTrib blockers fall from 521 to 0; other fiscal field findings remain evaluated; RTC mode is unchanged; no candidates or writes are produced.
@@ -0,0 +1,67 @@
# cClassTrib applicability gate
## Purpose
Stop treating an empty `C000025.CCLASSTRIB` as a fiscal defect solely because
the global RTC feature is enabled. Keep the single existing fiscal-completeness
result and continue evaluating every other fiscal field normally.
## Evidence and selected applicability rule
The SGV database has `PARAMETROS.USAR_REFORMA_TRIBUTARIA=1` and RTC-capable
schema, but the inspected `PARAMETROS` and `C000004` schemas have no separate
cClassTrib enable flag. The SGV product screen exposes `C000025.CCLASSTRIB` as
an editable catalog-backed field; available evidence does not mark it required
for every product. The current code incorrectly promotes global RTC capability
to universal per-product requiredness.
Use the live company regime (`C000004.CRT`) together with the documented
effective-date gate:
- RTC capability/`USAR_REFORMA_TRIBUTARIA` must still be enabled for RTC
evaluation generally.
- CRT 3: cClassTrib is applicable from 2026-08-03.
- CRT 1, 2, or 4: cClassTrib is applicable from 2027-01-01.
- Missing, conflicting, or unsupported CRT: cClassTrib is not applicable for
completeness until the company context is resolved; never infer a regime.
- Before the applicable date, ignore cClassTrib completely: no blocker, no
candidate, no completeness penalty, and no write.
- Once applicable, preserve the existing validation/resolution behavior,
including a blocker for an empty value when no safe resolution is available.
This makes current Agent 2 (`CRT=1`, `OPTANTE_SIMPLES=S`) not applicable on
2026-09-25 even though `USAR_REFORMA_TRIBUTARIA=1`. Its current RTC write mode
(`DRY_RUN`) is not changed.
## Minimal implementation boundary
Add the gate to the existing shared fiscal evaluator/context using the already
resolved live company CRT and current date. Do not add a new SGV flag, table,
panel, or second coverage metric. Keep PIS/COFINS, NCM, resolver rules, RTC
write authorization, and Agent write allowlist unchanged. Because the evaluator
is executed by the Agent, this is an Agent code change. After test completion,
the operator explicitly authorized release 1.2.5 through the official Hub/Agent
deploy path and an in-place Agent 2 upgrade that preserves its identity.
The RTC page should present the existing single-panel status as
“cClassTrib: Não utilizado neste cliente” while the gate is false.
## Verification
Add the requested focused tests: not-applicable with empty cClassTrib (RTC off
and on), unrelated fiscal fields still evaluated, applicable with empty
cClassTrib still blocked, and zero write attempts. Then run a read-only
`REPROCESS_ALL` against Agent 2 and compare its latest result/snapshot and
blockers with the captured baseline. Preserve `RTC_WRITE_MODE=DRY_RUN` and
verify Firebird write attempts/successes remain zero. Report NCM/PIS/COFINS
blockers independently.
## Baseline captured before implementation
Agent 2 latest Hub snapshot/job: total 521, complete 0, safe-blocked 521,
failed 0, unclassified 0, coverage 0%; RTC blockers 521; PIS blocker products
1; COFINS blocker products 1; NCM blocker products 26 (29 open blocker rows).
The offline read-only Firebird discovery saw `USAR_REFORMA_TRIBUTARIA=1`,
`SISTEMA_TODO_FISCAL=0`, `C000004.CRT=1`, and
`C000004.OPTANTE_SIMPLES='S'`; its guarded query recorded zero write attempts
and zero successes.