git-subtree-dir: fiscal git-subtree-mainline:d95165fc8fgit-subtree-split:102ba84500
8.9 KiB
Control Panel Operational UX V1
This document defines the operator-facing contracts for the existing
server-rendered GTI Fiscal control panel. It complements
CONTROL_PANEL_V1.md, which documents architecture,
identity, authentication, APIs, retention, and the explicit fixture command.
The UX work does not change fiscal rules, the Hub/Agent protocol, Firebird
access, AI write controls, or deployment.
Dashboard health and fiscal metrics
Technical health is summarized by exactly five states: ONLINE, DEGRADED,
OFFLINE, FAILED, and UNKNOWN. The dashboard total is reconciled from those
state counts and must always satisfy:
agents_total = online + degraded + offline + failed + unknown
Technical health describes Agent/installation availability and processing failures. Fiscal status and fiscal completion are separate: safe-blocked fiscal work does not make an otherwise healthy Agent technically degraded or failed. Agent views show product total, fiscally complete count, safe-blocked count, technical-failure count, unclassified residual, and fiscal completion percentage. At installation/snapshot grain, coverage is reconciled as:
TOTAL_PRODUCTS = COMPLETE + SAFE_BLOCKED + FAILED_PRODUCTS + UNCLASSIFIED
UNCLASSIFIED is the positive residual TOTAL_PRODUCTS - COMPLETE - SAFE_BLOCKED - FAILED_PRODUCTS: the snapshot did not assign those products to
one of its explicit states. It is an observability bucket, not a newly inferred
fiscal state. If explicit category counts exceed the total or contain negative
values, the panel shows an inconsistency instead of clamping it away. Fixture
snapshots assign all products explicitly and therefore reconcile to zero
unclassified products. The fiscal completion percentage is
fiscally_complete / total_products * 100, rounded to one decimal place and
bounded to 0–100; when the total is zero, the stored coverage percentage is
used. The fiscal status badge is BLOCKED when safe-blocked work exists,
COMPLETE when all products are complete, and otherwise INCOMPLETE.
Coverage-state counts and blocker-registry counts have different sources and must remain distinct:
- Safe-blocked coverage products: sum of the Agent snapshot safe-blocked count at installation grain; this participates in the coverage invariant.
- Products with open blockers: distinct open
(tenant_id, product_code)pairs in the blocker registry. - Open blockers: open blocker rows; one product can have multiple rows for different fields or evidence.
Dashboard attention is limited to ten items. Items are ordered by technical
health (FAILED, DEGRADED, OFFLINE, UNKNOWN), required-field blockers,
then outdated Agent versions; ties are stable. Safe-blocked products and
outdated versions do not imply technical failure. Failed jobs and outdated
Agents are also shown in compact sections, each with an explicit empty state
when there is no data. Dashboard health and fiscal summary cards remain visible
when lists are empty.
Identity and time presentation
Agents, jobs, and blockers lead with human identity: client, installation display name, and hostname where available. Technical numeric IDs and UUIDs remain secondary details. Agent tables distinguish technical health from fiscal status and provide a worst-fiscal-coverage sort. Job rows identify the client and installation and show job ID, kind, status, timestamps, duration, and error. Blocker rows identify the client/installation, product, field, blocker type, operational priority, age, and detail link.
Database and API timestamps remain UTC. UI timestamps render in
UI_TIMEZONE, defaulting to America/Bahia, as DD/MM/YYYY HH:MM with a
relative age alongside them; a full technical timestamp is available in the
element title where applicable. Missing timestamps render as -. Job duration
uses start/finish timestamps (em andamento if not finished, - if not
started); it does not replace UTC persistence or API values.
Blocker list, summary, and operational priority
The blocker page applies filters on the server: client, installation, field,
blocker type, status, age, and product/search. The filtered summary reports
open blocker rows, distinct affected (tenant_id, product_code) products,
distinct affected clients, and row breakdown by CFOP, ST, ICMS, CSOSN,
NCM, and OTHER. Filtered rows and their summary use the same filter set.
Priority is deterministic and operational only; it does not represent tax severity, fiscal risk, legal consequence, or a tax recommendation. The current tiers are:
CRITICAL: known technical/connection/job-failure blockers or technical and timeout reasons that prevent processing.HIGH: missing-evidence blocker types that require operator attention.NORMAL: recognized fiscal fields (CFOP,ST,ICMS,CSOSN,NCM) or insufficient/no-evidence reasons without a higher operational tier.LOW: other optional or reference-enrichment cases.
The tier changes ordering/triage only. It must not be described as invented tax severity.
Details, events, and safe operations
Agent details present client, display name, and hostname before technical IDs, separate technical/fiscal/version badges, and show human-formatted last-seen time plus company context. Fiscal summary links to filtered blockers. Event history supports server-side severity, type, date, job, and product filters with pagination; event rows expose related job/product links when present. Blocker details show evidence, first/last seen, retry count, related events/jobs, and resolution audit history when available.
Legacy blocker rows without a persisted installation binding remain visible at tenant level, but the panel does not offer context resolution or reprocessing for them. The detail page explains that an exact server cannot be proven; direct resolution attempts return a conflict rather than guessing an Agent.
Resolving a blocker means saving validated operator context and queueing
reprocessing for that product; it does not directly write fiscal data or
immediately mark historical blocker evidence resolved. The workflow requires a
human-authenticated session and valid CSRF token. Accepted scopes are
PRODUCT, GROUP, and COMPANY; the scope/key/value are validated using the
existing context-key rules, including enum restrictions, and the required
reason/note is length-limited. PRODUCT scope must use the blocker product
code as its scope key. Arbitrary fields or keys are rejected.
The transaction creates or updates the validated context entry, creates a
REPROCESS_PRODUCTS job whose product list contains only the blocker product,
and records an audit event. The audit context includes operator/admin ID,
tenant/client, company key, installation identifiers, product code, blocker ID,
context key/scope/scope key/value, reason, before/after values, reprocess job
ID, and request IP when available. Validation or persistence failure rolls back
the operation. The action cannot write to Firebird, execute SQL/shell, or
enable AI writes.
Installation-wide reprocessing has its own confirmation page at
/agents/{agent_id}/reprocess-confirmation; only an explicit confirmed POST
with CSRF creates or reuses the pending full-reprocess job and records an audit
event. The confirmation page is part of the operator workflow, not a browser
window.confirm dialog.
Authentication policy
Production administrator creation always requires the strong password policy.
Short/weak local development passwords are accepted only when
ALLOW_WEAK_DEV_PASSWORD=1 and APP_ENV is not production. The flag never
weakens production policy. Existing local administrator credentials are not
changed by the control panel.
Limits, responsive behavior, and boundaries
Dashboard metrics and lists use snapshots and bounded queries; the dashboard does not scan product tables. List pagination is capped at 100 rows per page; the UI requests 50 rows for Agents, Jobs, and Blockers, and 25 event rows per Agent detail page. Dashboard attention is capped at ten; recent failed-job rows are capped at ten. Blocker detail loads at most 100 related events/jobs/audits, and displays a compact subset of event/job rows. Filters and pagination are server-side.
The interface remains server-rendered with project-local CSS and minimal progressive enhancement. Layouts adapt to desktop, tablet, and narrow screens; wide data tables remain horizontally scrollable, focus states remain visible, and health/status meaning is not communicated by color alone. Empty states explain when there are no failed jobs, outdated Agents, blockers, or related events/jobs.
The explicit non-production fixture is generated with:
python -m central.scripts.generate_control_panel_fixture --agents 100 --confirm-non-production
The command preserves its non-production confirmation gate and is never run at startup or migration time. This UX introduces no AI writes, Firebird writes, production database changes, or deployment actions.