feat: zero-touch Agent — central scheduler, central pause, delta sync, discovery-driven installer

Hub: installations.processing_enabled / sync_interval_seconds / last_sync_* (migration 0026).
The first heartbeat of an installation without coverage queues exactly one INITIAL_SYNC;
afterwards one SYNC_INCREMENTAL per interval (idempotent, backs off on failure). One fiscal
job at a time per Agent (FIFO, stale RUNNING recovery). Job completion now updates the
coverage snapshot, blockers and JOB_STARTED/COMPLETED/FAILED events. Panel: Sincronizar
agora / Reprocessar tudo / Pausar / Retomar + status; pause is enforced Hub-side.
Agent: local AGENT_JOBS_ENABLED is ignored (central control); SYNC_INCREMENTAL re-classifies
only new/changed products via a per-product fingerprint+decision cache and rebuilds coverage
from it; results carry operational summary and blockers; Firebird credentials come from a
DPAPI firebird.bin. Setup: read-only SGV Firebird discovery (auto-select only when
unambiguous, wizard chooser otherwise), starts the service, waits for the first heartbeat,
upgrade-in-place mode. All jobs stay read-only (FIREBIRD_WRITES=0). Version 1.1.3.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
gti
2026-09-24 08:47:00 -03:00
co-authored by Claude Sonnet 5
parent 9f9e06e9e9
commit bb663a979c
36 changed files with 1409 additions and 118 deletions
+38
View File
@@ -465,3 +465,41 @@ version's code and run `alembic upgrade head` again (migrations are
additive, so this is safe going forward; there is no tested downgrade path
for `0006`, so don't `alembic downgrade` in a pilot without restoring from a
backup first).
## Zero-touch Agent (Setup 1.1.3+): install once, control from the panel
The operator runs `GTIFiscalAgent-Setup-<version>.exe` **once** and types the install
code. Nothing else happens on the client server — no PowerShell, no `agent.env` editing,
no `services.msc`, no per-client cron:
1. **Firebird discovery (read-only).** Setup finds the Firebird service and candidate
`.FDB` files and probes each one (read-only connection) for `C000025` + `C000004`.
Exactly one SGV database -> selected automatically. Several, none, or credentials
other than the SGV default -> the wizard asks *during the installation* (list / file
picker + user/password, verified before use). It never guesses among several.
2. **Enrollment** with the install code, then three protected files (DPAPI LocalMachine +
SYSTEM/Administrators ACL): `credentials.bin` (Hub token), `firebird.bin` (Firebird
user/password, **never sent to the Hub**), and a non-secret `agent.env`.
3. **Service** `GTIFiscalAgent`: Automatic Delayed Start, restart-on-failure, **started by
Setup**; it waits for the first heartbeat and tells the operator "nenhuma outra ação".
4. **First sync is automatic.** On the first heartbeat of an installation without
coverage the Hub queues exactly one `INITIAL_SYNC` (idempotent). Afterwards the Hub's
scheduler queues one `SYNC_INCREMENTAL` per interval (default 30 min,
`installations.sync_interval_seconds`), only when nothing is queued/running.
`SYNC_INCREMENTAL` re-classifies only new/changed products (per-product source
fingerprint cache in the Agent's SQLite) and rebuilds coverage from the cache.
`REPROCESS_ALL` runs only when an operator asks for it.
5. **Central control** (Agent detail page, no shell, no arbitrary commands):
*Sincronizar agora*, *Reprocessar tudo*, *Pausar / Retomar processamento*, plus status
(Ativo/Pausado, última/agendada sincronização, produtos alterados, último job).
Pause = the heartbeat keeps flowing but the Hub hands out no job and schedules none.
The old local `AGENT_JOBS_ENABLED` is ignored — there is no local toggle.
Guarantees: one fiscal job per Agent at a time (FIFO queue, a job stuck RUNNING > 1 h is
failed so it cannot block the queue), a failed sync backs off for the interval instead of
looping, a Hub or Firebird outage fails the job cleanly and the service keeps running.
Every job kind is **read-only on the ERP** (`FIREBIRD_WRITES=0`, `FISCAL_MODE=DRY_RUN`).
Upgrading an installed host: run the newer Setup again (no code needed). It replaces the
binaries, keeps `agent_id`/`installation_id`/credentials, removes the legacy local toggle and
restarts the service.