Metadata-Version: 2.4
Name: guardiancontext
Version: 0.1.1
Summary: GuardianContext — memória, regras e coordenação para agentes (humanos e IA) que dividem um repositório: contexto que não se perde nem custa a janela inteira.
Author: IronStack
License: Licença de Uso — GuardianContext
        Copyright (c) 2026 IronStack. Todos os direitos reservados.
        
        1. Concessão. A IronStack concede a você uma licença limitada, não exclusiva,
           intransferível e revogável para instalar e usar este software (CLI `guardian`,
           Desktop Manager e componentes distribuídos com eles) em conjunto com o serviço
           GuardianContext, conforme os Termos de Uso vigentes (docs/termos-de-uso.md ou
           https://api.ironstack.dev.br/termos), que integram esta licença.
        
        2. Restrições. Salvo autorização escrita da IronStack, você não pode: redistribuir,
           sublicenciar, vender ou alugar o software; remover avisos de autoria; usar o
           software para construir serviço concorrente; nem extrair a biblioteca curada de
           padrões para uso fora do serviço. Modificações para uso próprio são permitidas.
        
        3. Arquivos gerados no seu repositório (router.md, quadro.md, wiki/, decisoes/,
           claims/, handoffs/, hook pre-commit e afins) são seus, sem restrição.
        
        4. SEM GARANTIAS. O SOFTWARE É FORNECIDO "NO ESTADO EM QUE SE ENCONTRA", SEM GARANTIA
           DE QUALQUER NATUREZA, EXPRESSA OU IMPLÍCITA, INCLUINDO COMERCIALIZAÇÃO, ADEQUAÇÃO
           A UM FIM ESPECÍFICO E NÃO VIOLAÇÃO.
        
        5. LIMITAÇÃO DE RESPONSABILIDADE. EM NENHUMA HIPÓTESE A IRONSTACK RESPONDERÁ POR
           DANOS INDIRETOS, INCIDENTAIS, ESPECIAIS OU CONSEQUENCIAIS, PERDA DE DADOS, DE
           CÓDIGO OU DE LUCROS, DECORRENTES DO USO DO SOFTWARE OU DE AÇÕES DE AGENTES DE IA
           OPERADOS PELO USUÁRIO, AINDA QUE AVISADA DA POSSIBILIDADE. AGENTES DE IA EXECUTAM
           COMANDOS REAIS; A REVISÃO DO QUE ELES FAZEM É RESPONSABILIDADE DO USUÁRIO.
        
        6. Lei aplicável: República Federativa do Brasil.
        
Keywords: agents,coordination,git,multi-agent,control-plane
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: PyYAML>=6.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Provides-Extra: semantic
Requires-Dist: fastembed==0.5.1; extra == "semantic"
Provides-Extra: obsidian
Requires-Dist: watchdog>=3.0.0; extra == "obsidian"
Provides-Extra: desktop
Requires-Dist: PySide6>=6.6; extra == "desktop"
Dynamic: license-file

# GuardianContext

> **Nome**: GuardianContext (decidido em 17/09/2026; antes "AgentNexusPlatform", nome já
> tomado por outro produto). Pacote `guardiancontext`, CLI `guardian`, env `GUARDIAN_*`,
> estado local em `.guardian/` e `~/.guardian/`. Marca da IronStack.
>
> **Status: em produção** — `https://api.ironstack.dev.br` (FastAPI + PostgreSQL/pgvector, Caddy/HTTPS),
> rodando a revisão de 17/09 com billing validado no sandbox da Asaas
> (`docs/trabalho/billing-sandbox-e2e-2026-09-17.md`). VM renomeada em 17/09
> (`/opt/guardiancontext`, containers `guardiancontext-*`, banco/usuário `guardiancontext`, env `GUARDIAN_*`).

Produto: transformar em SaaS o protocolo de coordenação multi-agente
(roteador + wiki por tópico + decisões + quadro de trabalho) validado dentro
do repositório **GymAi Pro** entre 06/09/2026 e 14/09/2026. Este documento é o
handoff — tudo que foi decidido numa sessão de ideação longa, pra continuar
aqui sem arrastar aquele histórico nem misturar com o desenvolvimento do app.


## Instalar

Uma linha, sem falar com ninguém — instala o `uv` se faltar, o pacote
`guardiancontext[semantic,desktop]` (Python 3.11) e abre o **Desktop Manager**, onde você
cria a conta (com aceite dos Termos de Uso) e conecta o repositório:

```powershell
irm https://api.ironstack.dev.br/install.ps1 | iex        # Windows (PowerShell)
```
```bash
curl -fsSL https://api.ironstack.dev.br/install.sh | sh    # macOS / Linux
```

Passo a passo para quem vai testar: [docs/guia-do-testador.md](docs/guia-do-testador.md).
Só terminal: `uv tool install --python 3.11 "guardiancontext[semantic]"` e `guardian signup`.

## Licença e termos

Software sob licença proprietária de uso (`LICENSE`); o serviço, os planos e os avisos sobre
programação com agentes de IA estão em [`docs/termos-de-uso.md`](docs/termos-de-uso.md)
(versão 2026-09-20, aceita no cadastro). Contato: suporte@ironstack.dev.br.

## Por que isto é um produto

O GymAi Pro roda com várias sessões de IA (Claude Code, Codex, Cursor, Kimi)
editando a mesma árvore git ao mesmo tempo. Isso já causou perda de commit,
furo de segurança (~40 min aberto) e horas de diagnóstico errado. A solução —
uma base de conhecimento em markdown versionada no git (router → wiki por
tópico → decisões → quadro de trabalho), mais um manual de sobrevivência pra
git compartilhado — funcionou bem o suficiente pra virar tese de produto: com
o volume de vídeos no YouTube propondo variações do mesmo problema (memória
persistente pra IA de código), esta solução, medida, saiu na frente.

Documentos de referência publicados (Claude Artifacts — acessíveis fora deste
projeto, independente de onde a conversa aconteceu):

- **[Kit de Coordenação Multi-Agente](https://claude.ai/code/artifact/b1604567-bed3-4241-acc1-f4b43ecf4689)** — os dois prompts vendáveis: o Instalador (bootstrap da estrutura em qualquer repo) e o Auditor (disciplina recorrente, o que vira assinatura).
- **[Infra do Kit de Coordenação](https://claude.ai/artifact/EieUEVezZeYAQkFoYkTHdV)** — topologia Oracle (quente) × Locaweb (fria), schema MySQL completo, plano de implementação em 7 fases, funil de onboarding do cliente.

## Decisões já tomadas (não reabrir sem motivo novo)

1. **Não centralizar o artefato de coordenação em si.** `router.md`/`wiki/`/`decisoes/`/quadro continuam sendo arquivo, na árvore do cliente — é isso que dá zero-setup, diff em PR e funcionamento offline. Avaliamos e descartamos mover isso pra MySQL (recriaria o erro do "ai-memory"/Akita: segunda fonte de verdade divergindo do git).
2. **O que centraliza**: biblioteca de padrões curada pela empresa (não pelo cliente), dashboard de auditoria hospedado, licenciamento. Nunca o conteúdo bruto do cliente (incidente, decisão, segredo de arquitetura).
3. **Modelo de acesso ao repo do cliente**: Agente instalado (CLI local) primeiro, GitHub App como possível "modo enterprise" depois — não o contrário.
4. **Licenciamento nunca por chamada de ferramenta.** Validação 1×/sessão ou 1×/dia, com cache e graça offline. Isto é lição paga: o hook `PreToolUse` do graphify custava ~400ms por chamada de Bash/Grep/Read/Glob, aplicado à árvore inteira — medido e revertido em 13/09/2026 no próprio GymAi Pro.
5. **Banco: PostgreSQL (SaaS) + SQLite (agente local).** Revogado o MySQL. O PostgreSQL é a base única do servidor — ACID, JSONB compacto, FTS nativo (`tsvector`) e busca vetorial via `pgvector` para recuperação top-k (minimiza tokens), migrations via Alembic, replication/arquivamento. O SQLite (FTS5 + sqlite-vec) é apenas cache/índice offline do agente local, derivado do Git — nunca uma segunda fonte de verdade. Tabelas desenhadas por padrão de acesso, não por entidade pura.
6. **Topologia**: tudo que responde uma sessão em andamento (API, Redis, PostgreSQL primário) fica na VM Oracle, como mais um serviço no `docker-compose.yml` que já roda `hermes-agent`/`hermes-support`. Sem arquivo frio em fornecedor separado: o PostgreSQL é a base única, e o arquivamento/retenção sai de replicação e particionamento na própria instância — não de uma segunda infraestrutura com deploy manual.
7. **Funil**: cadastro → instalação local (funciona offline, nunca bloqueia) → primeiro valor imediato (auditoria roda na hora da instalação, dashboard não nasce vazio) → loop recorrente (hook de git ou cron do cliente) → conversão free→pago via Asaas (gatilho: 2º repositório) → equipe (licença por repositório instalado, não por pessoa). **Trial de 14 dias revogado em 20/09/2026** (`decisoes/2026-09-20-plano-free-um-repositorio.md`): a instalação nasce no plano `free`, sem prazo, limitado a 1 repositório por conta; o gatilho por tempo saiu.
8. **Locaweb como arquivo frio: REVOGADA em 14/09/2026.** A decisão de usar a Locaweb como destino de arquivo frio (deploy manual cPanel/FTP, nginx/PHP na frente, risco de `413` silencioso e `REMOTE_ADDR`/`HTTPS` não confiáveis) foi abandonada. Substituto definido: PostgreSQL (SaaS) + SQLite (agente local), conforme a decisão #5.

## Números medidos que sustentam o pitch (não são estimativa)

- `TRABALHO_EM_ANDAMENTO.md` do GymAi Pro chegou a **245 KB (~66 mil tokens) em 14 dias** — 43% de uma janela de 200k gastos antes de abrir um arquivo de código. Reestruturado (quadro = stub de 6 linhas, história em `docs/trabalho/<slug>.md`) para **20 KB (~5,6k tokens)**, 11,8× menor.
- Hook `PreToolUse` do graphify: **~400ms por chamada** de ferramenta, medido, removido.
- Graphify (grafo de código) no mesmo repo: **144,3× de redução de token** por consulta, medido via `graphify benchmark`.
- Divergência normal entre `main` local e `origin/main` num repo com múltiplas sessões de IA cherry-pickando cada uma o próprio commit: **83 vs. 71 commits**, quase tudo mesmo conteúdo com SHA diferente — isso é o comportamento esperado do fluxo, não bug (lição aprendida depois de eu mesma ler errado da primeira vez).

## Em aberto — próximos passos de ideação/execução

- Nome final do produto (este README usa "GuardianContext" como nome da pasta/projeto; não foi validado como nome comercial).
- Piloto: usar o próprio GymAi Pro como conta zero, comparando os números acima como caso de referência real.

## Estado da implementação (Fases 1–4 — núcleo local + backend + licenciamento + produção)

**Implementado, testado e deployado em produção** (`https://api.ironstack.dev.br`).

### Núcleo local (`guardiancontext` — pacote Python instalável)

Control plane Git-native. `pip install -e .` expõe o comando `guardiancontext`:

- `guardian init [--mode shared|branches]` — bootstrap idempotente da estrutura (router, quadro, wiki, decisões, claims, handoffs, histórico) e do modo de trabalho (ver "Modos de trabalho").
- `guardian doctor` — valida a estrutura; falha com exit≠0 se houver erro. Reporta o modo e se o hook está instalado.
- `guardian hook install|uninstall` — hook `pre-commit` (ver "Hook de pre-commit").
- `guardian audit` — auditoria determinística (sem LLM): arquivos ausentes, quadro inchado, claims expirados/conflitantes, handoffs abandonados, links quebrados.
- `guardian claim --actor X <escopo...>` — reserva de escopo de arquivos com TTL; **recusa sobreposição** de outro ator.
- `guardian release <id>` — libera um claim.
- `guardian handoff create/accept` — handoff com **aceite único** (segundo accept é recusado).
- `guardian index rebuild/search` — índice SQLite híbrido (FTS5 + vetores) derivado dos markdown. **Rebuild incremental por sha256**: só re-embedda páginas novas/alteradas e expurga as removidas (8 páginas sem mudança → 0.2s; antes ~5s). `--semantic` força o modelo; `--lexical` desativa; default é automático. Busca combina FTS5 + cosine com **RRF** (Reciprocal Rank Fusion).
- `guardian signup [--api-url URL] [--email E] [--name N] [--repo NOME] [--invite CODIGO]` — **o caminho normal de entrada** (20/09/2026, sem admin): cria a conta (senha via `getpass`, sem eco, nunca em argumento), a primeira instalação (repositório = nome da pasta atual, licença `free`) e grava `api_url` + `api_key` exatamente como o `configure`. Pergunta o que faltar; sem TTY e sem os argumentos, falha com a mensagem do flag. `api_url` default: `https://api.ironstack.dev.br`. 402 (limite do plano), 403 (convite obrigatório), 409 (e-mail já cadastrado), 429 (rate limit) viram uma linha legível. O JWT vive só na memória do comando — o que fica em disco é a API key.
- `guardian login [--api-url URL] [--email E] [--repo NOME] [--yes]` — entra numa conta existente: lista as instalações; com uma (ou `--repo`), oferece reemitir a chave e gravar aqui (as chaves antigas continuam valendo); sem nenhuma, cria a primeira (mesmo fluxo do `signup`). 401 = "e-mail ou senha inválidos", sem traceback.
- `guardian configure --api-url ... --api-key ...` — o **caminho manual**: grava uma chave emitida pelo admin (`POST /installations`) ou por outro `login`.
- `guardian sync-patterns` — sincroniza os patterns do índice local com o servidor. **Privacidade (decisão #2)**: envia apenas slug/título/sha256/embedding — nunca o corpo dos documentos. Upsert idempotente por sha256 no servidor (`repository_patterns`), com remoção de páginas deletadas.
- `guardian report [--actor X]` — roda a auditoria e envia o relatório (métricas apenas) ao servidor; **offline-safe** (enfileira se o servidor estiver fora).
- `guardian embed <texto>` — gera embedding local de 384 dimensões. Dois modos: **semântico** (`--semantic`, usa `paraphrase-multilingual-MiniLM-L12-v2` via `fastembed`, 100% local, funciona bem em português) ou **hashing determinístico** (`--hashing`, sem modelo externo). Auto-detecta o melhor disponível.
- `guardian license [--force]` — consulta o estado da licença (validação 1×/dia, com cache e graça offline — decisão #4).
- `guardian contribute <arquivo.md> [--yes] [--tags a,b]` — propõe uma decisão/regra deste repo à biblioteca curada. Única saída de corpo de documento (decisão #2): anonimiza localmente, **mostra o texto inteiro e pede confirmação** (sem TTY exige `--yes`), envia com embedding gerado no mesmo recorte do índice. Sem fila offline. O `report` avisa quando há `decisoes/`/`wiki/` novas ou alteradas desde o anterior (`.guardian/last_report.json`) — só aponta o comando, nunca envia.
- `guardian proposals list [--status pending|approved|rejected|all] [--json]` / `show <id> [--json]` / `approve <id> [--slug s] [--tags a,b]` / `reject <id> --reason "..."` — o lado do **curador** do laço de aprendizado: lista a fila (default pendentes; id, status, slug, repo/data, tamanho do corpo), mostra título/tags/corpo completo (já anonimizado), aprova (só metadados editáveis — slug e tags; o corpo sobe como foi lido; imprime o slug do pattern publicado) ou rejeita com motivo obrigatório. Usa a `api_url` do `configure`, mas o **token admin vem só de `GUARDIAN_ADMIN_TOKEN`** (nunca gravado em config, nunca impresso); sem a variável, sai com erro. 401/404/409 viram mensagens legíveis, sem traceback.

Testes: `pytest tests/` — 137 testes (59 + 21 de modos/hook + 17 de `proposals` + 29 de `signup`/`login` + 9 do desktop; 10 pulam sem `fastembed`/PySide6) (os de embedding semântico pulam se `fastembed` não estiver instalado). Embedding oficial: `fastembed==0.5.1` (pinado); vetores de outra versão do fastembed **não são comparáveis** (0.5.1 × 0.8.0 deram cosseno 0,565 para o mesmo texto).

### Modos de trabalho

Várias sessões de IA num repositório trabalham de um de dois jeitos, e o que é
normal num é sinal de erro no outro. O modo é declarado em `router.md` (linha
`Modo: shared` ou `Modo: branches`) — versionado, portanto visível para todas as
sessões — e lido de lá pelo `doctor` e pelo hook. Não fica em `.guardian/config.json`
porque isso é local a uma working copy.

- **`shared`** (default): várias sessões editam e comitam a **mesma working copy**.
  `init` grava o `wiki/como-trabalhar.md` completo — as regras de árvore compartilhada
  nascidas de incidente (nunca `git add -A`, nunca `stash`/`restore .`, branch+log antes
  de cada commit, cópia entre worktrees apaga, etc.).
- **`branches`**: cada sessão numa worktree/branch própria; a principal só recebe merge.
  `init --mode branches` grava uma versão enxuta: sincronize a principal antes de criar a
  branch, uma branch por sessão/tarefa (`guardian claim` continua avisando o escopo), nunca
  copie arquivo entre worktrees (cherry-pick), merge só por PR ou fast-forward, nunca
  `--force` na principal, claims/handoffs/quadro continuam sendo o canal entre sessões.
  As regras que independem do modo ficam ("verde não prova completude", "está na principal
  se prova por conteúdo").

`guardian init` sem `--mode` respeita o modo que o `router.md` já declara; com `--mode`
troca a linha do router (migração). O `como-trabalhar.md` de um repo já instalado **nunca**
é sobrescrito — trocar de modo depois é editar esse arquivo à mão. `guardian doctor`
imprime `[MODO] …` e avisa se o router não declara modo.

### Hook de pre-commit

`guardian hook install` grava um `pre-commit` (em `.git/hooks/`, ou onde `core.hooksPath`
apontar) de poucas linhas de sh POSIX — roda no Git Bash do Windows e em Linux/macOS — que
chama `guardian hook run pre-commit`. O trabalho fica em Python. A cada commit:

1. Imprime `[guardian] branch=<x> head=<sha7> arquivos=<n>` — a regra "branch + log antes
   de cada commit" cumprida pelo hook, não pela memória do agente.
2. **Recusa** se algum arquivo em staging está num claim **ativo de outro ator**. O ator da
   sessão vem de `GUARDIAN_ACTOR` (recomendado: uma variável por sessão), de
   `guardian hook run pre-commit --actor X` ou de `actor` em `.guardian/config.json`. Sem
   ator, avisa e não bloqueia por claim.
3. No modo `shared`, **recusa staging largo**: mais de N arquivos (default 25;
   `guardian hook install --max-files N`, `GUARDIAN_HOOK_MAX_FILES` ou `hook_max_files` na
   config) **e** nenhum claim do próprio ator cobre todos eles — o sinal de `git add -A`
   numa árvore compartilhada. No modo `branches` isso é normal e não é checado.

O escape é o do git, `git commit --no-verify`; não existe outro. Instalar duas vezes não
duplica (o bloco fica entre marcadores); um `pre-commit` de outra origem que seja shell
script recebe o bloco no fim e `uninstall` remove só o bloco; se não for shell (python,
node…), a instalação recusa e diz o que fazer.

### Desktop Manager (`guardian-desktop`, 20/09/2026)

GUI em PySide6 no mesmo pacote (extra `desktop`; pacote `guardiancontext_desktop/`). É o que o
instalador de uma linha abre: assistente de primeira execução (Criar conta com aceite dos Termos
/ Entrar → pasta do repositório git → modo → roda `init`, `hook install`, `index rebuild`,
`report`, `license` com log na tela → resumo) e janela principal (conexão, licença, repositórios
da conta, busca de patterns, "Instalar em outro repositório"). **Uma configuração só**: usa as
mesmas funções do CLI (`guardiancontext.account`, `sync.configure`) e a mesma
`~/.guardian/config.json`; só tema/pastas ficam em `~/.guardian/desktop.json`. Senha nunca em
disco, JWT só em memória. Sem binário (sem assinatura de código). Detalhes:
[`guardiancontext_desktop/README.md`](guardiancontext_desktop/README.md).

### Backend (FastAPI + PostgreSQL + pgvector + FTS + Alembic)

- `backend/app/` — API em FastAPI, SQLAlchemy 2.0 + psycopg.
- Modelos: `users`, `repositories`, `audit_events`, `api_keys`, `licenses`, `billing_events`, `patterns` (biblioteca curada com `embedding vector(384)` + `search_vector tsvector`), `pattern_proposals` (fila de propostas do `guardian contribute`).
- Busca full-text em português (`to_tsvector`/`websearch_to_tsquery`) e vetorial (`cosine_distance` via pgvector) sobre os padrões curados.
- Migrations Alembic (inclui `CREATE EXTENSION vector`).
- **Autenticação (Fase 2):** endpoints admin protegidos por `GUARDIAN_ADMIN_TOKEN` (fail-closed se vazio); endpoints do agente protegidos por API key (`api_keys`, SHA-256). Instalação via `POST /api/v1/installations` emite a chave uma única vez.
- **Self-service (20/09/2026):** `POST /auth/register` é público (rate limit em memória, 5/h por IP; `GUARDIAN_SIGNUP_INVITE_CODE` não vazio exige `invite_code` → 403) e **exige o aceite dos Termos de Uso**: `terms_version` igual a `settings.terms_version` (422 caso contrário), gravado em `users.terms_version`/`terms_accepted_at`. `GET /termos` (markdown, fora do `/api/v1`) serve o texto e `GET /api/v1/termos/versao` a versão. Os instaladores de uma linha também saem daqui: `GET /install.ps1` e `GET /install.sh` (`backend/app/static/`). Com o JWT: `POST /me/installations` (repo + chave + licença `free`, 402 no limite — mesma função interna do `/installations`), `GET /me/installations` (lista com plano/status, sem chaves), `POST /me/installations/{id}/keys` (reemite chave do próprio repo; 404 se não for dele; as antigas continuam válidas — revogação segue admin/SQL).
- **Licenciamento (Fase 3):** a instalação nasce no plano **`free`** — licença `ativo`, sem prazo, **1 repositório por conta** (`POST /installations` e `/installations/full` respondem 402 no segundo; plano criado sob demanda pelo código, sem migration). Segundo repositório exige assinatura ativa em plano pago via `/checkout` — vale então o `max_repositories` do plano (`None` = ilimitado). Repositórios `internal` não contam. Validação 1×/sessão ou 1×/dia (`GET /api/v1/license`), com cache e graça offline no agente (decisão #4). Licença por repositório (decisão #7). Licenças `trial` antigas continuam vencendo na data.
- **Licença interna (sem cobrança):** `POST /repositories/{id}/license/grant` (admin) → plano `internal`, ativa sem prazo, fora do Asaas. Para os projetos do próprio dono e cortesias.
- **Billing Asaas (Fase 3):** webhook `POST /api/v1/billing/webhook` autenticado pelo "Token de autenticação" do painel (header `asaas-access-token`, comparação em tempo constante, 503 sem `GUARDIAN_ASAAS_WEBHOOK_TOKEN`). A Asaas não assina o body. Idempotente por `id` do evento; responde 200 a todo payload válido (evento irrelevante → `ignored`), porque fora de 2xx a Asaas reenvia. Casa a licença por `payment.subscription`/`subscription.id` contra `asaas_subscription_id`. Mapeamento em `backend/app/services/asaas.py`. **Ainda não validado com um evento real** — o webhook no painel está inativo até o deploy.
- **Dashboard (Fase 3):** `GET /dashboard` (HTML) + `GET /api/v1/dashboard/summary` (métricas apenas — repositórios, usuários, auditorias, licenças por status, auditorias recentes; nunca conteúdo bruto).
- `docker compose up -d db` sobe `pgvector/pgvector:pg16`; `docker compose up api` sobe a API.
- **Login JWT:** `GUARDIAN_JWT_SECRET` próprio (separado do admin token), fail-closed; senha em bcrypt; admin token comparado em tempo constante. `POST /installations/full` e a leitura da biblioteca curada (`GET /patterns`, `/patterns/search`, `/patterns/similar`) exigem admin token ou API key.
- Testes: `backend/.venv/Scripts/python -m pytest backend/tests/` (precisa de Postgres + `GUARDIAN_JWT_SECRET`) — 71 passam. CI em `.github/workflows/ci.yml`: suíte do CLI, uma head no Alembic, migrations num banco vazio e no estado de produção, suíte do backend.

### O que ainda NÃO existe (fora do corte até a Fase 4)

- GitHub App (modo enterprise) — ainda só o agente local (decisão #3).
- Publicação no PyPI (pacote validado: build + twine check + smoke em venv limpo; conta criada, token salvo em `guardiancontext.pypirc`; falta `twine upload` com credenciais do usuário).

## Laço de aprendizado — o produto aprende com quem usa (20/09/2026)

A biblioteca curada cresce com decisões reais dos repositórios instalados, sem quebrar a decisão #2: o `report` compara `decisoes/` e `wiki/` com o report anterior e **só avisa** (`guardian contribute <caminho>`); o dono roda o comando, lê o texto já anonimizado e confirma. O servidor recebe em `POST /api/v1/patterns/proposals` (API key do repo), **roda o anonimizador de novo** e recusa com 422 se sobrar identificador; a proposta fica `pending` em `pattern_proposals`. Um admin lista (`GET /patterns/proposals?status=pending`), lê (`GET /patterns/proposals/{id}`), aprova (`POST .../{id}/approve`, com edições opcionais — vira `Pattern` com FTS e o vetor da proposta; a resposta traz `pattern_slug`) ou rejeita (`POST .../{id}/reject`, nota obrigatória) — pelo CLI, `guardian proposals list|show|approve|reject` com `GUARDIAN_ADMIN_TOKEN` no ambiente. Lista de identificadores proibidos em `guardiancontext/anonymizer.py` (cópia verificada por teste em `backend/app/services/anonymizer.py`). Registro: `docs/trabalho/laco-de-aprendizado-2026-09-20.md`.

## Produção (Fase 4 — deploy + webhook + instalação)

**Deployado e validado em produção** na VM Oracle (Ubuntu ARM64):

- **`https://api.ironstack.dev.br`** — API FastAPI + Postgres/pgvector via `docker compose`, atrás do Caddy (HTTPS/Let's Encrypt automático, `caddy reload` sem downtime). Subdomínio `api.ironstack.dev.br` aponta para a VM via DNS A na Locaweb.
- **Webhook Asaas cadastrado no painel, inativo**: URL `POST /api/v1/billing/webhook`, API v3, envio não sequencial, token de autenticação gerado. Ativar só depois do deploy do handler novo (o anterior exigia HMAC, que a Asaas não envia — recusaria 100% dos eventos).
- **Instalação de produção criada**: repo `GuardianContext` (id=1), owner `rogerio@ironstack.dev.br` (id=1), API key `gc_…` emitida via `POST /api/v1/installations`.
- **CLI local conectado**: `guardian doctor` → 0 erros; `guardian index rebuild` → 8 páginas indexadas, embeddings semânticos funcionando (`paraphrase-multilingual-MiniLM-L12-v2` via `fastembed`, 100% local); `guardian index search "webhook HMAC validation"` → top-k com snippets e similaridade cosine.
- **Configuração do CLI**: `guardian signup`/`guardian login` (ou, manualmente, `guardian configure --api-url ... --api-key ...`) gravam `api_url` em `<repo>/.guardian/config.json` e a **API key em `~/.guardian/config.json`** (0600) — nunca dentro do repositório; chave que apareça no config do repo é ignorada. Precedência: `GUARDIAN_API_KEY`/`GUARDIAN_API_URL` no ambiente > `~/.guardian` > repo. `GUARDIAN_HOME` redireciona o diretório do usuário (testes/CI).

O guia operacional completo (deploy Docker, variáveis, setup admin, instalação do
agente, publicação no PyPI, webhook Asaas, backup, modelo de ameaças) está em
[docs/runbook-producao.md](./docs/runbook-producao.md).

## Como retomar uma sessão aqui

Leia este README primeiro — ele substitui a necessidade de reler a conversa
original (que ficou no repositório GymAi Pro, sessão longa de 06 a 14/09/2026).
Os dois artefatos linkados têm o conteúdo completo de prompts/schema/plano; não
precisam ser recriados.
