diff --git a/docs/superpowers/specs/2026-07-13-opc-gateway-design.md b/docs/superpowers/specs/2026-07-13-opc-gateway-design.md index 3979d09..8cf451d 100644 --- a/docs/superpowers/specs/2026-07-13-opc-gateway-design.md +++ b/docs/superpowers/specs/2026-07-13-opc-gateway-design.md @@ -63,7 +63,7 @@ Mongo `sientia`: ### Escrita hoje (Laborious) - Escrita OPC **direta** via `asyncua` (`laborious/activities/opc.py` → `opc_repository.write_data` → `node.write_value`). -- Status de escrita é gambiarra: sobrescreve `prediction_confidence` com sentinelas (`12`=erro escrita, `14`=sessão ruim; PI=`13`) em `process_confidence`. Predições persistidas em Postgres `predictions_schema.predictions` (`model_id, prediction, prediction_confidence, response_time, prediction_status, timestamp, created_at, comments`). +- Status de escrita é gambiarra: sobrescreve `prediction_confidence` com sentinelas (`12`=erro escrita, `14`=sessão ruim; PI=`13`) em `process_confidence`. Predições persistidas em Postgres (`model_id, prediction, prediction_confidence, response_time, prediction_status, timestamp, created_at, comments`) via **pandas `to_sql`** (schema/tabela por-modelo, criados por inferência do DataFrame; `predictions_schema.predictions` é só fixture de teste). - Payload de escrita já contém: `server_id`, `addr`, `value`, `data_type`, `tag_type`, `model_id`, `model_name`, `timestamp`. ## 5. Arquitetura do Gateway (Abordagem B — engines por capacidade) @@ -123,13 +123,16 @@ Laborious publica, Gateway consome. Core NATS, subject `opc.write` (avaliar `opc ```json { "msg_id":"uuid","server_id":"server1","addr":"ns=2;i=5","value":42.0, "data_type":"float","tag_type":"prediction","model_id":"1","model_name":"m1", + "schema":"sientia_data","table_name":"predictions_", "timestamp":"2026-07-13T12:00:00Z","write_timestamp":true,"write_quality":false } ``` +`schema`+`table_name` são necessários porque a tabela de predição é **por-modelo** — o Gateway usa-os para o `UPDATE` da coluna `write_status` (ver C3). ### C3 — Coluna de status de escrita (Postgres, tabela de predições) ⭐ autoritativo em topics.md Nova coluna no DDL padrão de predições (ex.: `write_status` + opcional `write_status_detail`/`write_status_at`). Em **escrita OPC**, o **Gateway** grava o status vinculando por **`timestamp` + `model_id`**. Em **escrita PI Web API**, o **Laborious** grava. Substitui as sentinelas `12/13/14` em `prediction_confidence`. - **Onde:** schema/tabela de predição são **config por modelo** (`input_data['schema']`/`['table_name']` no Laborious) — não há tabela única fixa. A coluna faz parte do **DDL padrão** de predições; homologar no schema gerenciado pelo Flyway (**`sientia_data`**). Migração via `Aignosi/sientia-dataops-database-migrations` (`.sql` versionado, Job Helm hook), backward-compatible. -- **Provisionamento (CONFIRMADO):** as tabelas de predição são **criadas dinamicamente por modelo**. Logo a coluna `write_status` entra no **template de criação** dessas tabelas (novas) + **backfill** (`ALTER TABLE ... ADD COLUMN IF NOT EXISTS`) nas existentes. Flyway cobre a migração de backfill/tabelas conhecidas; o template dinâmico precisa passar a incluir a coluna. Localizar onde o DDL de criação vive (Laborious/`sientia_do` Postgres/migrations) — ver §17. +- **Provisionamento (LOCALIZADO):** **não há DDL/template** — as tabelas são criadas por **pandas `DataFrame.to_sql(if_exists='append')`** em `sientia_do/temporal/activities/postgres.py:439-464`, com o **schema inferido das colunas do DataFrame**. Logo: (a) **tabelas novas** ganham `write_status` automaticamente quando o DataFrame de predições do Laborious passar a incluir a coluna (trabalho do 1975); (b) **tabelas existentes** precisam de **backfill** `ALTER TABLE . ADD COLUMN IF NOT EXISTS write_status ...` — como são dinâmicas/por-modelo, a migração Flyway enumera as tabelas existentes (ex.: via `information_schema`) ou usa migração repetível/callback. +- **Fluxo de gravação:** o Laborious grava a linha de predição (via `to_sql`) com `write_status` default (ex.: `pending`/null); depois o Gateway faz `UPDATE . SET write_status=... WHERE timestamp=... AND model_id=...`. Por isso a mensagem NATS (C2) carrega `schema`+`table_name` (a tabela é por-modelo). ### C4 — GRPC leitura on-demand ``` @@ -224,17 +227,16 @@ Nota de plataforma: os docs air-gapped diziam *"do not include RabbitMQ/NATS unl - **Broker = NATS** (Core NATS agora; §8). Definido pelo brief/Vitor. - **Nome da branch:** renomeada para `feature/SIENTIAPDE-1972-...` ✅. -**Resolvidas (docs/código + Vitor):** -- **Migração = Flyway** — repo `Aignosi/sientia-dataops-database-migrations`, schema `sientia_data` (substituiu Alembic/SQLAlchemy). Coluna = `.sql` versionado (V2+), Job Helm hook. -- **Bucket de certs = `sientia-dataops`** (decisão Vitor). Escrita dos certs do 1569 migra p/ esse bucket. (§7) +**Resolvidas (docs/código + Vitor + Bruno):** +- **Migração = Flyway** — repo `Aignosi/sientia-dataops-database-migrations`, schema `sientia_data`. Coluna = `.sql` versionado, Job Helm hook. +- **Bucket de certs = `sientia-dataops`** (aprovado por Bruno). Ação: migrar a escrita dos certs do 1569 p/ esse bucket + policy (impl, não decisão). (§7) - **JOIN do 1974 = chave `tag_id`** (o pipeline referencia `tag_id`; `(server+address)` é ambíguo sob multi-nome). (§15) - **`metadata` = campo (objeto) na tag, default vazio** (decisão Vitor); tags de teste com `metadata: {}`. (§4) +- **Tabelas de predição = pandas `to_sql` (sem DDL/template)** — coluna nova via DataFrame do Laborious (tabelas novas) + backfill `ALTER TABLE` (existentes). (§6 C3) **Em aberto (precisam de humano):** 1. **Provisionamento do NATS no air-gapped:** subchart/versão/sizing/network policy (a *escolha* já é NATS). (§8) — **plataforma/Bruno** -2. **Escrita dos certs 1569 → `sientia-dataops`:** ajuste no app 1569 + policy do user do frontend p/ o bucket DataOps. (§7) — **plataforma** -3. **Template de criação das tabelas de predição:** confirmado que são dinâmicas por modelo → coluna `write_status` no template + backfill das existentes. Localizar onde vive o DDL de criação (Laborious/`sientia_do`/migrations) e definir o backfill. (§6 C3) — **Vitor / dono do repo de migrations** (ou localizo no código ao escrever o spec do 1975) -4. **Chamador do GRPC on-demand:** quem chama `ReadTag` e modelo de auth. — **Vitor** +2. **Chamador do GRPC on-demand:** quem chama `ReadTag` e modelo de auth. — **Vitor** **Adiada por design (nós):** - **Durabilidade da escrita:** Core NATS agora; reavaliar JetStream (TTL curto) se houver perda real em restart.