# PLANO HERMES LM PUBLISHER — IMPLEMENTAÇÃO COMPLETA NO LENOVO
**11/08/2026 · Para executar em sessões Codex, por copy-paste, no VS Code ligado ao `bellatrix-node`.**
**6 sessões (H-0 a H-5), por ordem. Cada uma tem critérios de aceitação — não passes à seguinte sem os verdes.**

---

## A ARQUITETURA, NUMA PÁGINA

```
DESKTOP (produção)                    LENOVO bellatrix-node (publicação, 24/7)
──────────────────                    ─────────────────────────────────────────
edita · aprova · planeia      ──►     utilizador lmpub (novo, isolado)
sincroniza media + calendário          ├─ HERMES LM  ←→  Telegram (bot próprio)
                                       │    cérebro: OpenAI → Gemini → NVIDIA
lê /estado/PUBLICACOES.json   ◄──      ├─ toolbelt lmpub (CLI c/ validações)
pela ponte HTTPS                       │    Buffer · Graph FB · Metricool
                                       ├─ timer 15/15 min (acorda o Hermes)
                                       └─ estado: CALENDARIO + PUBLICACOES

utilizador lenovod (intocado): publish-bridge (funnel 8791) + Hermes comercial
```

**Decisões fechadas pelo dono:** o Hermes publica diretamente (sem fila de ordens) · isolamento por **utilizador de sistema** `lmpub` · gatilho por cron que acorda o agente · biblioteca enviada de uma vez, reenvio só quando houver aprovações novas · o Lenovo é dono do estado, o desktop lê pela ponte · bot Telegram novo · notificações densas e nunca repetitivas.

## O QUE SÓ TU PODES FAZER (a checklist de autenticações)
Faz isto **antes** de arrancar, com 15 minutos. Nada disto se cola em chats — vai direto para os ficheiros que a H-2/H-3 criam:

| # | O quê | Onde |
|---|---|---|
| 1 | **Bot Telegram** → guarda o token e o teu chat_id | @BotFather → `/newbot` (ex.: `lm_shirt_publisher_bot`) |
| 2 | **Buffer access token** | publish.buffer.com/developers/api |
| 3 | **Facebook Page token** (`pages_manage_posts`, página 1326814130505190) | developers.facebook.com/tools/explorer |
| 4 | **Metricool user token** (se o plano deixar) | app.metricool.com → definições → API |
| 5 | **OpenAI API key** (a "chave Codex") | platform.openai.com/api-keys |
| 6 | **Gemini API key** (fallback) | aistudio.google.com/apikey |
| 7 | **NVIDIA NIM key** (último fallback) | build.nvidia.com |

---

# SESSÃO H-0 — DESCOBERTA (só lê, não muda nada)
**Codex, no Lenovo. ~20 min. É a sessão que evita todo o troubleshooting futuro: em vez de instalar o Hermes "em teoria", vais copiar o padrão do que JÁ FUNCIONA nesta máquina.**

```
Trabalha no bellatrix-node como lenovod. SESSÃO SÓ DE LEITURA — não alteras,
não instalas, não reinicias NADA. O Hermes comercial e o OpenClaw que aqui
correm estão em produção e não podem ser tocados.

MISSÃO: mapear como o Hermes existente está instalado e a correr, para a
próxima sessão REPLICAR o mesmo padrão num utilizador novo e isolado.

Descobre e documenta:
1. O software exato: que binário/repo é o "Hermes"? Versão? De onde foi
   instalado (git clone? pip? npm? docker?)? Caminho completo.
   Pistas: systemctl --user list-units | grep -iE "hermes|claw" ·
   ps aux | grep -iE "hermes|claw" · ls ~lenovod, ~/.config, /opt, docker ps
2. Como arranca: unit file completo do serviço (ExecStart, WorkingDirectory,
   Environment), ou compose/container se for docker.
3. Configuração: onde vive o config (caminhos), QUE CAMPOS tem — em especial
   como liga ao Telegram, como define o LLM/chave, como carrega ferramentas/
   MCP/Composio. NÃO copies valores de segredos — só os NOMES dos campos.
4. Suporta múltiplos fornecedores de LLM com fallback? Suporta correr duas
   instâncias (flag de perfil, порт, config dir alternativo)?
5. Que portas usa? (ss -tlnp) Para a instância nova não colidir.
6. O publish-bridge: unit file, pasta servida, versão do http-server.
7. Recursos da máquina: RAM/CPU livres com tudo a correr (free -h, nproc,
   uptime) — cabe uma segunda instância?

ESCREVE ~/INVENTARIO_HERMES.md com tudo isto, incluindo os comandos que
usaste e os outputs relevantes (segredos SEMPRE redigidos como <REDACTED>).
No fim, responde a UMA pergunta em maiúsculas no topo do ficheiro:
"A INSTÂNCIA NOVA DEVE SER INSTALADA ASSIM: <resumo de 5 linhas>".
```

**Aceitação:** `INVENTARIO_HERMES.md` existe, identifica o software, o padrão de arranque e as portas, e diz explicitamente como replicar.

---

# SESSÃO H-1 — FUNDAÇÃO (utilizador, isolamento, estado)
**Codex, no Lenovo. ~30 min.**

```
Trabalha no bellatrix-node. Vais criar a fundação do LM Publisher.
Lê primeiro ~/INVENTARIO_HERMES.md (da sessão H-0).

1. UTILIZADOR ISOLADO
   sudo useradd -m -s /bin/bash lmpub
   sudo loginctl enable-linger lmpub        # serviços sobrevivem sem sessão
   Sem sudo para o lmpub. Sem grupo partilhado com lenovod além do default.

2. ÁRVORE DE TRABALHO (como lmpub)
   /home/lmpub/lm/
     estado/        # CALENDARIO_PUBLICACAO.json · PUBLICACOES.json · APROVACOES_DONO.json · shirts_db.json
     toolbelt/      # o CLI (sessão H-2)
     metricas/      # séries semanais
     logs/
     hermes/        # a instância (sessão H-3)

3. TRAZER O ESTADO (uma vez)
   Copia do desktop (ou pede ao dono se o desktop estiver desligado):
   SISTEMA\CALENDARIO_PUBLICACAO.json, PUBLICACOES.json, APROVACOES_DONO.json,
   shirts_db.json, PLANO_ROTACAO.md, AGENTE_LM_PUBLISHER.md → /home/lmpub/lm/estado/
   A partir de agora A CÓPIA DO LENOVO É A AUTORITATIVA para PUBLICACOES.json.

4. EXPOR O ESTADO NA PONTE (leitura, para o desktop planear sem duplicar)
   Como lmpub:  chmod 755 /home/lmpub /home/lmpub/lm /home/lmpub/lm/estado
                chmod 644 /home/lmpub/lm/estado/*.json
   Como lenovod: ln -s /home/lmpub/lm/estado /home/lenovod/publish-bridge/media/estado
   PROVA: curl -s https://bellatrix-node.tail321fa0.ts.net/estado/PUBLICACOES.json | head
   tem de devolver o JSON. Se o http-server não seguir symlinks, reinicia-o
   com a flag adequada ou serve via hardcopy sincronizada por systemd path-unit
   — mas resolve AGORA, não deixes para depois.

5. ACESSO À MEDIA
   A media está em /home/lenovod/publish-bridge/media/<pasta>/ e é pública via
   funnel. O toolbelt do lmpub usa OS URLS HTTPS, nunca caminhos locais de
   outro utilizador — zero permissões cruzadas.

ESCREVE /home/lmpub/lm/README.md com o mapa disto tudo.
```

**Aceitação:** `id lmpub` ok · linger ativo · `curl .../estado/PUBLICACOES.json` devolve o registo · README escrito.

---

# SESSÃO H-2 — O TOOLBELT `lmpub` (as mãos, com as validações embutidas)
**Codex, no Lenovo, como `lmpub`. ~2 h. É a peça mais importante do sistema.**

```
Trabalha como lmpub em /home/lmpub/lm/toolbelt. Python 3, stdlib + requests.
Vais construir o CLI `lmpub` — as mãos do agente. TODA a publicação passa por
aqui; o agente nunca chama APIs de redes sociais por outra via.

CONFIG: /home/lmpub/lm/.env  (chmod 600) — cria com chaves VAZIAS:
  BUFFER_ACCESS_TOKEN=            BUFFER_CHANNEL_IG=6a402eab5ab6d2f1067c7536
  FB_PAGE_ID=1326814130505190     FB_PAGE_ACCESS_TOKEN=
  METRICOOL_USER_ID=5151954       METRICOOL_BLOG_ID=6695614   METRICOOL_USER_TOKEN=
  TELEGRAM_BOT_TOKEN=             TELEGRAM_CHAT_ID=
  BRIDGE=https://bellatrix-node.tail321fa0.ts.net
Nunca imprimas valores destas chaves em logs ou stdout.

SUBCOMANDOS (todos com --dry-run COMO DEFAULT; publicar a sério exige --live):

lmpub slot [--live]
  Lê estado/CALENDARIO_PUBLICACAO.json. Slots com data==hoje e hora vencida
  (tolerância 90 min). Por cada um, o ROTEIRO DOS 5 CANAIS (abaixo).

lmpub publicar <pasta> [--quando ISO|agora] [--canais lista] [--live]
  A mesma máquina para uma peça avulsa.

lmpub estado        → últimos publicados, próximos slots, falhas pendentes
lmpub metricas [--dias 7]  → views/reach/likes por post (IG Graph + Metricool)
lmpub reordenar <pasta> <data>  → troca slots; recusa se violar regras da fila
lmpub vinted drop|closet|sold <id>  → rotação (shirts_db.json + PLANO_ROTACAO.md)
lmpub validar       → chamadas de LEITURA a todos os canais; relatório verde/vermelho
lmpub reconciliar --desde AAAA-MM-DD
  A CURA DO ESTADO VELHO: lista via IG Graph (e Metricool) tudo o que foi
  realmente publicado desde a data, compara com estado/PUBLICACOES.json, e
  ACRESCENTA as entradas em falta (metodo:"reconciliacao"). Nunca apaga.
  Corre OBRIGATORIAMENTE no arranque (H-5, antes do 1.º slot) com
  --desde = data do _SNAPSHOT.txt — assim um snapshot USB desatualizado
  deixa de poder causar duplicados: a verdade reconstrói-se da própria rede.

VALIDAÇÕES OBRIGATÓRIAS antes de qualquer publish (a ordem importa):
  1. pasta tem veredicto APROVADO em estado/APROVACOES_DONO.json
  2. pasta NÃO consta de estado/PUBLICACOES.json (qualquer formato)  ← anti-dup
  3. slot não está "bloqueado" e não existe _NAO_PUBLICAR
  4. HEAD ao BRIDGE/<pasta>/reel.mp4 = 200 e Accept-Ranges: bytes
  Falha uma → o comando RECUSA com o motivo em uma linha. Sem exceções.

ROTEIRO DOS 5 CANAIS (um falhar nunca trava os seguintes):
  1. IG reel   → Buffer API: POST /1/updates/create com media video url +
                 legenda integral de BRIDGE/<pasta>/legenda.txt, now=true
  2. FB reel   → Graph: POST /<page>/videos {file_url, description, title}
                 Se "could not be read": repetir UMA vez (certificado funnel).
  3. TikTok    → Metricool: POST /v2/scheduler/posts (X-Mc-Auth) autoPublish,
                 legenda curta = 1.ª frase + 5 hashtags. CONTADOR mensal:
                 aos 18/20 pára e marca aviso.
  4. Story capa→ Buffer, type story, imagem BRIDGE/<pasta>/capa.jpg
  5. Story reel→ Buffer, type story, o vídeo do reel.
                 REGRA DURA: stories de vídeo SEMPRE por Buffer (o caminho
                 Graph/Composio dá ERROR genérico — provado 2x a 10/08).

REGISTO: após CADA sucesso (não no fim), acrescenta a entrada a
estado/PUBLICACOES.json — atómico (.tmp + os.replace). Formato igual às
entradas existentes (projeto, pasta_biblioteca, clube, canal, formato,
publicado_em, permalink, metodo, nota).

TRAVAS GLOBAIS: máx 4 slots por execução · timeout IG: NUNCA repetir publish
sem verificar estado primeiro · lock file (~/lm/.lmpub.lock) para nunca
correrem duas instâncias em simultâneo.

TESTES (obrigatórios, sem rede: mock das APIs):
  test_antidup, test_bloqueado, test_canal_falha_nao_trava_os_outros,
  test_registo_atomico, test_quota_metricool, test_lock.
  pytest verde antes de fechares a sessão.

TIMER: systemd user units lmpub-tick.service (oneshot: lmpub slot --live) e
lmpub-tick.timer (OnCalendar=*:00/15, Persistent=true). enable --now.
NOTA: o timer executa o calendário mesmo que o Hermes (H-3) esteja em baixo —
é a rede de segurança "publica sempre". O Hermes acrescenta cérebro, não é
ponto único de falha.
```

**Aceitação:** `lmpub validar` corre (vermelho é aceitável sem tokens) · `lmpub slot` em dry-run mostra o plano do próximo slot · pytest verde · timer ativo.

---

# SESSÃO H-3 — O HERMES LM (o cérebro + Telegram)
**Codex, no Lenovo, como `lmpub`. ~2 h. Depende do INVENTARIO_HERMES.md.**

```
Trabalha como lmpub. Vais instalar a instância LM do Hermes seguindo o
INVENTARIO_HERMES.md (auditoria de 10/08) — o padrão exato que já funciona
nesta máquina. FACTOS CONFIRMADOS que usas tal e qual:

INSTALAÇÃO (replica o padrão pip/venv):
  python3.12 -m venv /home/lmpub/.hermes-venv
  /home/lmpub/.hermes-venv/bin/pip install hermes-agent==0.19.0
  HERMES_HOME=/home/lmpub/lm/hermes  (config.yaml, .env, auth.json próprios,
  chmod 600, NUNCA copiados nem reutilizados da instância comercial)

UNIT (decalca a hermes-gateway.service auditada, com 3 mudanças):
  ExecStart=/home/lmpub/.hermes-venv/bin/python -m hermes_cli.main gateway run
  WorkingDirectory + Environment HERMES_HOME apontados ao home novo
  Restart=always, RestartSec=5, ExecStopPost do cgroup_cleanup — IGUAIS
  + LIMITES (a máquina tem 2 CPUs e load ~2 — isto é obrigatório):
    CPUWeight=40  MemoryMax=2G  Nice=10
  (aplica os mesmos limites ao lmpub-tick.service da H-2)

CONFIG (campos confirmados no inventário):
  model.provider/default → OpenAI (a chave "Codex")
  fallback_providers → [{gemini}, {nvidia}]   ← fallback NATIVO, não faças wrapper
  telegram: TELEGRAM_BOT_TOKEN + TELEGRAM_HOME_CHANNEL + TELEGRAM_ALLOWED_USERS
    no .env (o bot NOVO; allowed_users = SÓ o chat_id do dono)
  command_allowlist → SÓ comandos que começam por "lmpub"   ← nativo, usa-o
  security.redact_secrets: true · approvals.mode: nenhum pedido de aprovação
    para comandos lmpub (a validação vive no toolbelt)
  platform_toolsets.telegram: mínimo — terminal restrito; SEM web, SEM browser
  mcp_servers: NENHUM. Zero Composio nesta instância — as mãos são o toolbelt.

O Hermes não escuta TCP (confirmado) — não há porta a gerir. NÃO toques em
18789 (OpenClaw), 8791 (ponte), 7070, 11434.

SYSTEM PROMPT: o ficheiro /home/lmpub/lm/estado/AGENTE_LM_PUBLISHER.md,
verbatim. É a constituição — não a resumas nem a "melhores".

FERRAMENTAS DO AGENTE: expõe-lhe o toolbelt como ferramentas shell:
  lmpub slot|publicar|estado|metricas|reordenar|vinted|validar
E MAIS NADA. Sem shell arbitrário, sem browser, sem acesso a ficheiros fora
de /home/lmpub/lm. Se o framework obrigar a shell genérico, restringe por
allowlist de comandos que começam por "lmpub ".

DESPERTAR: systemd timer lmhermes-wake a cada 15 min injeta a mensagem:
"Rotina: corre lmpub estado. Se houver slot vencido que o timer ainda não
publicou, corre lmpub slot --live. Se houver falhas pendentes, trata-as
segundo a constituição §5. Se não houver nada, termina em silêncio."
(A publicação normal é feita pelo lmpub-tick; tu és a camada que pensa —
falhas, reordenações, avisos.)

AGENDA FIXA do agente:
  09:30 pulso diário (constituição §6 — SÓ se houver matéria)
  domingo 20:00 relatório semanal (§7)

PROVA FINAL DA SESSÃO (sem publicar nada):
  1. mandas "estado?" pelo Telegram → responde com o resumo real
  2. mandas "publica o teste" → deve RECUSAR citando a validação que falhou
  3. systemctl --user status nos dois serviços + timers: tudo ativo
  4. simula queda do OpenAI (chave errada temporária) → cai para Gemini e regista
```

**Aceitação:** bot responde só ao dono · os 4 testes da prova passam · serviços com restart automático (`Restart=on-failure`) e linger.

---

# SESSÃO H-4 — MÉTRICAS, RELATÓRIOS, VINTED
**Codex, como `lmpub`. ~1 h.**

```
1. lmpub metricas: implementa a recolha real — IG media insights (views,
   reach, likes, saved, shares) via Graph com o FB_PAGE_ACCESS_TOKEN;
   TikTok via Metricool se houver token. Guarda snapshot diário em
   ~/lm/metricas/AAAA-MM-DD.json e agregado semanal AAAA-SS.json.
   Régua separada para reels e carrosséis — nunca na mesma média.
2. Pulso e relatório: templates no toolbelt (o Hermes só os preenche):
   pulso = 3 linhas máx; semanal = tabela melhor→pior + média vs base
   (Jan-Abr ~1400 · pré-medição ~415) + leitura numa frase + semana seguinte.
   MARCO 24/08 hardcoded: se média das 21:00 < 800 ao 8.º post, o relatório
   diz "o problema não é a hora, é o conteúdo" com os números.
3. lmpub vinted: drop (10/semana), closet (2/semana), sold <id> (remove de
   tudo). Lê/escreve shirts_db.json, segue PLANO_ROTACAO.md. Stories Vinted
   via Buffer com CTA "link na bio".
4. Healthcheck diário 08:00 (systemd timer): valida os 5 canais em leitura +
   espaço em disco + funnel vivo. SÓ manda Telegram se algo estiver vermelho.
```

**Aceitação:** `lmpub metricas` devolve números reais dos posts de 10/08 · healthcheck instalado e silencioso quando está tudo verde.

---

# SESSÃO H-5 — COMISSIONAMENTO (o dia em que passa a ser a sério)
**Codex + o dono. ~40 min. Só depois de os tokens estarem no `.env`.**

```
1. lmpub validar → OS 5 CANAIS VERDES (leitura). Falta algum token? PÁRA
   e diz ao dono qual. Sem verdes não há passo 2.
1b. lmpub reconciliar --desde <data do estado/_SNAPSHOT.txt> → o livro-razão
   fica alinhado com o que REALMENTE saiu nas redes desde o snapshot USB.
   Mostra ao dono o que acrescentou. SEM ISTO NÃO HÁ PASSO 2.
2. Ensaio geral: lmpub slot (dry-run) do próximo slot real → mostra o plano
   completo dos 5 canais ao dono NO TELEGRAM. Ele responde "aprovo".
3. Primeiro slot AO VIVO com supervisão: no dia, o slot sai pelo timer;
   confirma os 5 permalinks + o registo em PUBLICACOES.json + a linha de
   confirmação no Telegram.
4. Desligar o legado: confirmar com o orquestrador do desktop que os
   agendamentos manuais (Buffer/cron do desktop) desta e das próximas datas
   foram cancelados — A PARTIR DAQUI SÓ O LENOVO PUBLICA.
5. Semana de rodagem: 7 dias com o dono a receber tudo. No fim, retro de
   uma página: o que falhou, o que o agente resolveu sozinho, o que mudar
   na constituição.
ESCREVE ~/lm/COMISSIONAMENTO.md com o resultado de cada passo.
```

**Aceitação:** um slot real publicado nos 5 canais sem intervenção manual, registado, confirmado no Telegram.

---

## GARANTIAS ANTI-TROUBLESHOOTING (o que compra a tua paz)
- **Dois motores:** o timer `lmpub-tick` publica o calendário **mesmo com o Hermes em baixo**. O agente é cérebro, nunca ponto único de falha.
- **Tudo systemd** com `Restart=on-failure` + `Persistent=true` + linger → sobrevive a reboots, quedas de luz, crashes.
- **Healthcheck 08:00** que só fala quando algo está vermelho — os problemas anunciam-se sozinhos antes da hora de publicar.
- **Validações no toolbelt, não no prompt** → uma alucinação do agente esbarra numa recusa, nunca num post errado.
- **Cascata de LLM** (OpenAI → Gemini → NVIDIA) → um fornecedor em baixo não cala o bot.
- **Lock + máx 4/execução + anti-dup no livro-razão** → o pior cenário é não publicar, nunca publicar duas vezes.

## DEPOIS DISTO, O TEU FLUXO É:
aprovas conteúdo aqui → o orquestrador sincroniza media+calendário para a ponte → **o Lenovo faz o resto** → tu recebes uma linha no Telegram por publicação e mandas ordens quando te apetecer.
