From 8130c4fbdb35ab8f9ec93bcf7e1b206e776d4cbf Mon Sep 17 00:00:00 2001 From: gti Date: Mon, 28 Sep 2026 11:22:18 -0300 Subject: [PATCH] =?UTF-8?q?docs:=20Hub/Agent=20independent=20versioning=20?= =?UTF-8?q?(Miss=C3=A3o=204=20/=20Bloco=20B.3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documents what the code already does (no behavior change): GTI_FISCAL_VERSION (Hub) and the Agent's own version are separate numbers with separate release cycles; central.app.installation_identity.reference_agent_version() already compares an Agent's heartbeat version against the highest PUBLISHED Windows AgentRelease, never against the Hub's version - verified against test_version_reference.py. Also documents that there is no real minimum- version connection gate today (only SCHEMA_FINGERPRINT is enforced) and where one would go if ever needed. Co-Authored-By: Claude Sonnet 5 --- docs/VERSIONING.md | 57 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 docs/VERSIONING.md diff --git a/docs/VERSIONING.md b/docs/VERSIONING.md new file mode 100644 index 0000000..e544d58 --- /dev/null +++ b/docs/VERSIONING.md @@ -0,0 +1,57 @@ +# Versionamento — Hub e Agent (Missão 4 / Bloco B.3) + +Hub e Agent têm números de versão **independentes**, cada um com seu próprio ciclo de release. +Um deploy do Hub (`scripts/deploy_hub.ps1`) nunca exige tocar no Agent, e vice-versa. Este +documento só descreve o comportamento já existente no código — nada foi alterado no Agent. + +## Fontes de verdade + +- **Hub**: `release/VERSION` == `central/app/version.py::GTI_FISCAL_VERSION` == + `central/pyproject.toml`'s `version` == `RELEASE_MANIFEST.json`'s `GTI_FISCAL_VERSION`/`hub.version`. + Exposto em `GET /version`. +- **Agent**: `agent/pyproject.toml`'s `version` == o default de `AGENT_VERSION` em + `agent/scripts/run_agent_service.py` == `AGENT_VERSION_DEFAULT` em + `agent/src/gti_fiscal_agent/preflight.py`. Em produção, o valor real vem de `.env` + (`AGENT_VERSION=...`), nunca do default do código — o default só importa para uma instalação + legada sem essa variável. +- Os dois só coincidem por coincidência numérica ocasional; **não há relação obrigatória entre + eles**. `tests/test_release_candidate.py` tem dois testes separados que provam isso: + `test_versions_are_coherent_across_hub_and_packages` (só Hub) e + `test_agent_version_is_internally_coherent` (só Agent, consigo mesmo). + +## Como o painel decide "Atualização disponível" (já implementado, sem mudança nesta missão) + +`central/app/installation_identity.py::reference_agent_version(session)` retorna a maior versão +entre os `AgentRelease` com `status=PUBLISHED` e `platform=windows` — **nunca** a versão do Hub. +Só cai de volta para `settings.agent_server_version` (env `CURRENT_SERVER_VERSION`, um valor de +compatibilidade legado, não a versão do Hub) quando não existe nenhum release Windows publicado. + +`compute_version_drift(agent_version, reference)` compara a versão relatada pelo Agent com essa +referência: `CURRENT` se `agent_version >= reference`, `OUTDATED` caso contrário. Exemplo real +verificado nesta missão: Hub em 1.3.11, Agent 2 em 1.3.8, e o último `AgentRelease` Windows +publicado também é 1.3.8 → `compute_version_drift("1.3.8", "1.3.8") == "CURRENT"`, **sem alerta +falso**, mesmo com o Hub numericamente "à frente". Testado em +`central/tests/test_version_reference.py`. + +## Versão mínima do Agent aceita pelo Hub + +Não existe hoje um bloqueio "Agent versão X não pode nem se conectar" — qualquer Agent +autenticado (token válido) pode enviar heartbeat e reivindicar Jobs, independentemente da +versão. O único controle de compatibilidade real hoje é `SCHEMA_FINGERPRINT` (a impressão do +schema Firebird que o Agent relata), não a versão de software. `api_compat` em `GET /version` +(hoje `"1.0"`) existe como um número de versão de **contrato de API**, mas nenhum código ainda +recusa uma chamada por causa dele — é informativo. + +### Matriz de compatibilidade (estado atual, não uma trava de código) + +| Hub | Agent | Compatível? | Observação | +|--------|--------|--------------|------------| +| 1.3.11 | 1.3.8 | Sim | Estado real de produção nesta missão. | +| 1.3.9+ | 1.1.0+ | Sim | Capabilities (`RTC_CCLASSTRIB_WRITE_V1`, enriquecimento) são negociadas por presença de campo no heartbeat, não por número de versão. | +| — | < 1.1.0 | Não testado | Antes da distribuição via installer/enrollment; fora do escopo de missões recentes. | + +Se um dia for necessário recusar Agents antigos de verdade (ex.: um campo obrigatório novo no +heartbeat), o mecanismo natural é acrescentar uma verificação explícita em +`central/app/api.py` (heartbeat/claim) comparando `agent_version` a um mínimo configurável — isso +NÃO existe hoje e não foi adicionado nesta missão (mudaria o comportamento do Agent/Hub sem +necessidade concreta).