Pipeline completo de publicação no LinkedIn: evaluator → redator → editor → art → director → publisher - Seed com 37 posts em _sugestoes.md - Sorteio de formato com N=3 bloqueados (format-history) - Reciclagem mensal de posts com rotação de formato - Revisão via Telegram com chat livre (Gemini 2.5 Flash) - Publicação via LinkedIn API (OAuth2) - Makefile com targets para Windows/Linux/ARM64 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
180 lines
5.0 KiB
Markdown
180 lines
5.0 KiB
Markdown
# ldpost-squad — Instruções Gerais
|
|
|
|
---
|
|
|
|
## Princípio Central: Testabilidade Independente
|
|
|
|
**Cada CLI deve rodar de forma isolada, sem depender que outro CLI tenha rodado antes.**
|
|
|
|
Regras:
|
|
- Toda CLI aceita `--post <slug>` como entrada principal
|
|
- Toda CLI aceita `--dry-run` para operar sem side effects (sem salvar arquivo, sem chamar API externa)
|
|
- Toda CLI pode ser testada com um fixture manual (arquivo criado à mão no workspace)
|
|
- Nenhuma CLI assume estado global — lê e escreve apenas no workspace do post
|
|
|
|
---
|
|
|
|
## Workspace Layout
|
|
|
|
```
|
|
$LDPOST_WORKSPACE/
|
|
<slug>/
|
|
post.json ← metadados: tema, formato, imagens desc, status
|
|
draft.md ← rascunho cru (gerado pelo redator)
|
|
final.md ← versão formatada LinkedIn (gerado pelo editor)
|
|
img/
|
|
cover.png ← imagem principal
|
|
slide-*.png ← slides do carrossel (se aplicável)
|
|
status.json ← pipeline state: pending|approved|published + URL
|
|
```
|
|
|
|
**Como testar qualquer CLI isoladamente:**
|
|
```bash
|
|
# Crie o fixture mínimo manualmente
|
|
mkdir -p "$LDPOST_WORKSPACE/meu-slug"
|
|
echo '{"slug":"meu-slug","topic":"IA no RH","format":"lista"}' \
|
|
> "$LDPOST_WORKSPACE/meu-slug/post.json"
|
|
|
|
# Agora rode qualquer CLI contra esse slug
|
|
ldpost-redator --post meu-slug --dry-run
|
|
ldpost-editor --post meu-slug --no-interactive
|
|
ldpost-art --post meu-slug --dry-run
|
|
ldpost-director --post meu-slug --skip-telegram
|
|
ldpost-publisher --post meu-slug --dry-run
|
|
```
|
|
|
|
---
|
|
|
|
## Variáveis de Ambiente
|
|
|
|
Arquivo `.env` na raiz do workspace **ou** exportadas no shell.
|
|
|
|
```env
|
|
# Obrigatórias
|
|
GROQ_API_KEY= # modelo rápido (redator, evaluator)
|
|
GEMINI_API_KEY= # imagens e análise (art, editor)
|
|
LDPOST_WORKSPACE=C:\Textos-LinkedIn
|
|
|
|
# Telegram (director)
|
|
TELEGRAM_BOT_TOKEN=
|
|
TELEGRAM_CHAT_ID=
|
|
|
|
# LinkedIn (publisher) — opcional, fallback manual
|
|
LINKEDIN_ACCESS_TOKEN=
|
|
LINKEDIN_CLIENT_ID=
|
|
LINKEDIN_CLIENT_SECRET=
|
|
```
|
|
|
|
Cada CLI deve chamar `godotenv.Load()` no init para carregar `.env` automaticamente se presente.
|
|
|
|
---
|
|
|
|
## Estrutura de Módulo Go
|
|
|
|
Cada CLI é um módulo Go **independente** dentro de `C:\gocode\jobmaker-ldpost\`:
|
|
|
|
```
|
|
jobmaker-ldpost/
|
|
shared/ ← módulo compartilhado (go.mod: ldpost/shared)
|
|
evaluator/ ← go.mod: ldpost/evaluator
|
|
redator/ ← go.mod: ldpost/redator
|
|
editor/ ← go.mod: ldpost/editor
|
|
art/ ← go.mod: ldpost/art
|
|
director/ ← go.mod: ldpost/director
|
|
publisher/ ← go.mod: ldpost/publisher
|
|
```
|
|
|
|
CLIs dependem de `ldpost/shared` via `replace` no `go.mod`:
|
|
```
|
|
require ldpost/shared v0.0.0
|
|
replace ldpost/shared => ../shared
|
|
```
|
|
|
|
---
|
|
|
|
## Convenções de Código
|
|
|
|
### Flags padrão (toda CLI implementa)
|
|
| Flag | Tipo | Descrição |
|
|
|------|------|-----------|
|
|
| `--post` | string | Slug do post (obrigatória na maioria) |
|
|
| `--dry-run` | bool | Opera sem salvar/publicar |
|
|
| `--model` | string | Override do modelo LLM |
|
|
| `--workspace` | string | Override de `LDPOST_WORKSPACE` |
|
|
| `--verbose` | bool | Log detalhado |
|
|
|
|
### Saída padrão
|
|
- Sucesso: imprime caminho do arquivo gerado ou URL publicada
|
|
- Erro: `fmt.Fprintf(os.Stderr, "erro: %v\n", err)` + `os.Exit(1)`
|
|
- Dry-run: imprime conteúdo que seria gerado/enviado
|
|
|
|
### Arquivo `post.json` (schema mínimo)
|
|
```json
|
|
{
|
|
"slug": "ia-no-rh-2026",
|
|
"topic": "IA no recrutamento em 2026",
|
|
"format": "lista",
|
|
"images": [
|
|
{"index": 1, "prompt": "robô entrevistando humano, estilo flat design"},
|
|
{"index": 2, "prompt": "dashboard de triagem de currículos com IA"}
|
|
],
|
|
"created_at": "2026-05-02T11:00:00Z"
|
|
}
|
|
```
|
|
|
|
### Arquivo `status.json`
|
|
```json
|
|
{
|
|
"slug": "ia-no-rh-2026",
|
|
"pipeline_status": "pending",
|
|
"steps_completed": ["evaluated", "drafted", "edited"],
|
|
"approved_at": null,
|
|
"published_url": null
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Padrões do jobmaker-squad (reutilizar)
|
|
|
|
Referência: `C:\gocode\jobmaker-squad\`
|
|
|
|
| Padrão | Onde está | O que reusar |
|
|
|--------|-----------|-------------|
|
|
| Gemini API calls | `jobmaker-redator/main.go` | Stream + retry pattern |
|
|
| Imagen (geração de imagem) | `jobmaker-art/main.go` | Gemini Imagen 3.0 call |
|
|
| Telegram bot | `jobmaker-editor/internal/telegram/` | Keyboard inline, await response |
|
|
| Config loader | `jobmaker-editor/internal/config/` | `.env` + flags merge |
|
|
| Vector store local | `jobmaker-editor/internal/vectorstore/` | chromem-go pattern |
|
|
|
|
---
|
|
|
|
## Como Rodar Testes
|
|
|
|
```bash
|
|
# Cada CLI
|
|
cd evaluator && go test ./... -v
|
|
|
|
# Shared
|
|
cd shared && go test ./... -v
|
|
|
|
# Smoke test completo (requer .env configurado)
|
|
./scripts/smoke-test.sh meu-slug
|
|
```
|
|
|
|
---
|
|
|
|
## Ordem de Implementação
|
|
|
|
Seguir essa ordem — cada passo desbloqueia o próximo:
|
|
|
|
1. `shared/` — structs + workspace utils
|
|
2. `evaluator/` — sem este, nenhum slug existe
|
|
3. `redator/` — depende de `post.json` do evaluator
|
|
4. `editor/` — depende de `draft.md` do redator
|
|
5. `art/` — depende de `post.json` (descriptions de imagem)
|
|
6. `director/` — depende de `final.md` + imagens
|
|
7. `publisher/` — depende de `status.json` aprovado
|
|
|
|
Mas cada um pode ser desenvolvido/testado **isoladamente** com fixtures manuais.
|