chore: limpa arquivos temporários e cache
Remove: - Cache do aider (.aider.tags.cache.v4) - Arquivos de planejamento antigos (.planning/) - Arquivos duplicados (MAPA_TENANTS_ARCADIA copy, SESSAO_2026-03-18) - Metadata de agentes (.agents/) Refs: cleanup, maintenance
This commit is contained in:
parent
14e440cc1f
commit
6182a9c490
|
|
@ -1,38 +0,0 @@
|
||||||
uploads = []
|
|
||||||
generated = []
|
|
||||||
|
|
||||||
[[outputs]]
|
|
||||||
id = "28NDwt4UT3HPglsSw-IQk"
|
|
||||||
uri = "file://MAPA_MICROSERVICOS_COOLIFY.md"
|
|
||||||
type = "text"
|
|
||||||
title = "Mapa de Microserviços - Arcádia Suite para Coolify/Docker"
|
|
||||||
|
|
||||||
[[outputs]]
|
|
||||||
id = "YTVCn39myPk5kzF8smbQz"
|
|
||||||
uri = "file://MAPA_SOE_ARCADIA.md"
|
|
||||||
type = "text"
|
|
||||||
title = "Mapa SOE — Sistema Operacional Empresarial (Deploy)"
|
|
||||||
|
|
||||||
[[outputs]]
|
|
||||||
id = "pQjx1X6m9hp2j-sZa0_Rg"
|
|
||||||
uri = "file://MAPA_BI_ARCADIA.md"
|
|
||||||
type = "text"
|
|
||||||
title = "Mapa BI - Business Intelligence da Arcádia Suite (com Metabase)"
|
|
||||||
|
|
||||||
[[outputs]]
|
|
||||||
id = "gpyYTqB0ytdU1zAJij6KK"
|
|
||||||
uri = "file://MAPA_CRM_ARCADIA.md"
|
|
||||||
type = "text"
|
|
||||||
title = "Mapa CRM - Sistema de Relacionamento da Arcádia Suite"
|
|
||||||
|
|
||||||
[[outputs]]
|
|
||||||
id = "zal1KfcZdP7OqKEXu67Pp"
|
|
||||||
uri = "file://MAPA_TENANTS_ARCADIA.md"
|
|
||||||
type = "text"
|
|
||||||
title = "Mapa Multi-Tenant - Gestão de Tenants da Arcádia Suite"
|
|
||||||
|
|
||||||
[[outputs]]
|
|
||||||
id = "GCMgcBAHQ1aIDjHxoXniQ"
|
|
||||||
uri = "file://MAPA_AUTOMACOES_ARCADIA.md"
|
|
||||||
type = "text"
|
|
||||||
title = "Mapa Automações - Motor de Automação da Arcádia Suite"
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,475 +0,0 @@
|
||||||
# Fase 4: OpenClaw Embutido — Plano de Implementação
|
|
||||||
|
|
||||||
**Fase:** 4
|
|
||||||
**Data Início:** 2026-03-26
|
|
||||||
**Prazo:** 2026-04-08 (Semanas 7-8)
|
|
||||||
**Status:** 🔄 Sprint 3 Pendente (Sprint 1 e 2 concluídos)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. OBJETIVO DA FASE
|
|
||||||
|
|
||||||
Implementar **detecção proativa de padrões** e **sugestão emergente de skills**, transformando comportamentos repetitivos do usuário em automações reutilizáveis através de um widget conversacional flutuante.
|
|
||||||
|
|
||||||
### Entregável Final
|
|
||||||
✅ Skills emergentes funcionando end-to-end:
|
|
||||||
- Usuário executa ação repetida → PatternDetector detecta → OpenClawWidget sugere → Blackboard gera DRAFT → Dev Center aprova → Skill disponível
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. ARQUITETURA
|
|
||||||
|
|
||||||
```
|
|
||||||
FLUXO OPENCCLAW:
|
|
||||||
|
|
||||||
1. USER INTERACTION
|
|
||||||
└─ Usuário usa Arcádia
|
|
||||||
└─ Learning API registra evento (já existe)
|
|
||||||
└─ Neo4j armazena interação
|
|
||||||
|
|
||||||
2. PATTERN DETECTION (NOVO)
|
|
||||||
└─ PatternDetector analisa Learning API
|
|
||||||
└─ Identifica: 3+ ocorrências em 30 dias
|
|
||||||
└─ Calcula confiança (threshold 0.8+)
|
|
||||||
└─ Dispara evento no Socket.IO
|
|
||||||
|
|
||||||
3. WIDGET SUGGESTION (NOVO)
|
|
||||||
└─ OpenClawWidget fica "ouvindo"
|
|
||||||
└─ Recebe evento de padrão detectado
|
|
||||||
└─ Exibe notificação flutuante
|
|
||||||
└─ Modal SkillSuggestion mostra preview
|
|
||||||
|
|
||||||
4. SKILL GENERATION (NOVO)
|
|
||||||
└─ Usuário confirma
|
|
||||||
└─ OpenClawEngine chama Blackboard
|
|
||||||
└─ Blackboard gera código (DRAFT)
|
|
||||||
└─ Armazena em database
|
|
||||||
|
|
||||||
5. APPROVAL FLOW (EXISTENTE - Dev Center)
|
|
||||||
└─ Skill DRAFT aparece em Dev Center
|
|
||||||
└─ Admin/Lead revisa e aprova
|
|
||||||
└─ Publica no tenant
|
|
||||||
└─ Skill fica disponível para reutilização
|
|
||||||
```
|
|
||||||
|
|
||||||
### Componentes para Implementar
|
|
||||||
|
|
||||||
```
|
|
||||||
BACKEND:
|
|
||||||
server/modules/openclaw/
|
|
||||||
├── PatternDetector.ts ← [NOVO] Detecta padrões
|
|
||||||
├── SkillEmergence.ts ← [NOVO] Coordena criação
|
|
||||||
├── OpenClawEngine.ts ← [NOVO] Lógica central
|
|
||||||
├── config/
|
|
||||||
│ └── arcadia.config.yaml ← [NOVO] Configuração
|
|
||||||
|
|
||||||
FRONTEND:
|
|
||||||
client/src/components/openclaw/
|
|
||||||
├── OpenClawWidget.tsx ← [NOVO] Widget flutuante
|
|
||||||
├── PatternDetector.ts ← [NOVO] Hook de detecção
|
|
||||||
├── SkillSuggestion.tsx ← [NOVO] Modal de sugestão
|
|
||||||
└── useAgentEmergence.ts ← [NOVO] Lógica de emergência
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. DEPENDÊNCIAS VERIFICADAS
|
|
||||||
|
|
||||||
| Componente | Status | Localização |
|
|
||||||
|-----------|--------|-----------|
|
|
||||||
| **Skills Engine** | ✅ Pronto | `server/skills/engine.ts` |
|
|
||||||
| **Blackboard** | ✅ Pronto | `server/blackboard/service.ts` |
|
|
||||||
| **Dev Center** | ✅ Pronto | `client/src/pages/DevCenter.tsx` |
|
|
||||||
| **Learning API** | ✅ Existe | `/api/learning/*` |
|
|
||||||
| **Neo4j (KG)** | ✅ Running | Docker |
|
|
||||||
| **Socket.IO** | ✅ Configurado | `server/socket-io.ts` |
|
|
||||||
| **PostgreSQL** | ✅ Rodando | Docker |
|
|
||||||
| **Scientist (MiroFlow)** | ✅ Existe | `/api/manus/scientist/*` |
|
|
||||||
|
|
||||||
**Conclusão:** Todas as dependências estão prontas ✅
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. TASKS DETALHADAS
|
|
||||||
|
|
||||||
### SPRINT 1 (26-27/03): PatternDetector Backend — ✅ COMPLETO
|
|
||||||
|
|
||||||
#### Task 1.1: Criar PatternDetector.ts ✅
|
|
||||||
**Arquivo:** `server/modules/openclaw/PatternDetector.ts`
|
|
||||||
**Objetivo:** Analisar eventos de usuário e detectar padrões
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Pseudocódigo da estrutura
|
|
||||||
export class PatternDetector {
|
|
||||||
// Config
|
|
||||||
min_occurrences: 3;
|
|
||||||
time_window_days: 30;
|
|
||||||
confidence_threshold: 0.8;
|
|
||||||
|
|
||||||
// Métodos
|
|
||||||
detectPattern(userId: string, action: string): Pattern | null
|
|
||||||
calculateConfidence(interactions: Interaction[]): number
|
|
||||||
storePattern(pattern: Pattern): Promise<void>
|
|
||||||
emitEvent(pattern: Pattern): void
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Entrada:** Eventos do Learning API (`/api/learning/interactions`)
|
|
||||||
**Saída:** Pattern objects armazenados em Neo4j + Socket.IO event
|
|
||||||
**Dependência:** Learning API funcionando ✅
|
|
||||||
|
|
||||||
#### Task 1.2: Criar OpenClawEngine.ts ✅
|
|
||||||
**Arquivo:** `server/modules/openclaw/OpenClawEngine.ts`
|
|
||||||
**Objetivo:** Orquestração central de OpenClaw
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
export class OpenClawEngine {
|
|
||||||
patternDetector: PatternDetector;
|
|
||||||
skillEmergence: SkillEmergence;
|
|
||||||
|
|
||||||
// Métodos
|
|
||||||
async suggestSkill(pattern: Pattern): Promise<SkillSuggestion>
|
|
||||||
async onUserConfirmation(suggestion: SkillSuggestion): Promise<Skill>
|
|
||||||
async checkPatternsPeriodicly(): Promise<void>
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Entrada:** Padrões detectados
|
|
||||||
**Saída:** Sugestões + Skills gerados
|
|
||||||
**Evento:** Socket.IO broadcast `/openclaw/pattern-detected`
|
|
||||||
|
|
||||||
#### Task 1.3: Criar SkillEmergence.ts ✅
|
|
||||||
**Arquivo:** `server/modules/openclaw/SkillEmergence.ts`
|
|
||||||
**Objetivo:** Integração com Blackboard para codegen
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
export class SkillEmergence {
|
|
||||||
async generateSkillDraft(pattern: Pattern): Promise<SkillDraft> {
|
|
||||||
// 1. Montar prompt baseado no padrão
|
|
||||||
// 2. Chamar POST /api/blackboard/generate-skill
|
|
||||||
// 3. Receber código gerado
|
|
||||||
// 4. Salvar como DRAFT no database
|
|
||||||
// 5. Notificar Dev Center
|
|
||||||
// 6. Retornar skill DRAFT
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Integração:** POST `/api/blackboard/generate-skill`
|
|
||||||
**Database:** skills table (com status: 'draft')
|
|
||||||
|
|
||||||
#### Task 1.4: Criar rotas OpenClaw ✅
|
|
||||||
**Arquivo:** `server/modules/openclaw/routes.ts`
|
|
||||||
**Rotas:**
|
|
||||||
```
|
|
||||||
POST /api/openclaw/detect-patterns ← Trigger manual de análise
|
|
||||||
GET /api/openclaw/patterns ← Listar padrões detectados
|
|
||||||
POST /api/openclaw/confirm-skill ← Usuário confirma sugestão
|
|
||||||
GET /api/openclaw/suggestions ← Histórico de sugestões
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Task 1.5: Integrar em server/index.ts ✅
|
|
||||||
**Arquivo:** `server/index.ts`
|
|
||||||
**Ação:** Importar rotas OpenClaw e carregá-las
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import openclawRoutes from "./modules/openclaw/routes";
|
|
||||||
app.use("/api/openclaw", openclawRoutes);
|
|
||||||
```
|
|
||||||
|
|
||||||
### SPRINT 2 (28-29/03): Frontend Widgets — ✅ COMPLETO (commit 5231166)
|
|
||||||
|
|
||||||
#### Task 2.1: Criar OpenClawWidget.tsx ✅
|
|
||||||
**Arquivo:** `client/src/components/openclaw/OpenClawWidget.tsx`
|
|
||||||
**Objetivo:** Widget flutuante que recebe notificações de padrões
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
export function OpenClawWidget() {
|
|
||||||
// Escutar Socket.IO event: /openclaw/pattern-detected
|
|
||||||
// Exibir notificação flutuante (canto inferior direito)
|
|
||||||
// Mostrar padrão detectado
|
|
||||||
// Botão "Ver Sugestão" abre modal
|
|
||||||
// Botão "Ignorar" descarta
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="fixed bottom-4 right-4">
|
|
||||||
{/* Widget flutuante */}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Socket.IO:** Escuta evento `/openclaw/pattern-detected`
|
|
||||||
**Trigger:** Modal SkillSuggestion
|
|
||||||
|
|
||||||
#### Task 2.2: Criar SkillSuggestion.tsx ✅
|
|
||||||
**Arquivo:** `client/src/components/openclaw/SkillSuggestion.tsx`
|
|
||||||
**Objetivo:** Modal com preview da skill sugerida
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
export function SkillSuggestion({ pattern, suggestion }) {
|
|
||||||
// Modal mostra:
|
|
||||||
// 1. "Detectamos um padrão na sua utilização:"
|
|
||||||
// 2. Preview do padrão (ações repetidas)
|
|
||||||
// 3. Preview da automação gerada
|
|
||||||
// 4. Campo de nome (auto-preenchido)
|
|
||||||
// 5. Descrição (auto-gerada)
|
|
||||||
// 6. Botão "Criar Skill" ou "Cancelar"
|
|
||||||
|
|
||||||
const handleCreate = async () => {
|
|
||||||
// POST /api/openclaw/confirm-skill
|
|
||||||
// Esperar resposta (skill DRAFT)
|
|
||||||
// Toast: "Skill criada! Vá para Dev Center para publicar"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Integração:** Chama `POST /api/openclaw/confirm-skill`
|
|
||||||
**Feedback:** Toast notification
|
|
||||||
|
|
||||||
#### Task 2.3: Hook useAgentEmergence.ts ✅
|
|
||||||
**Arquivo:** `client/src/hooks/useAgentEmergence.ts`
|
|
||||||
**Objetivo:** Lógica de emergência de agentes
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
export function useAgentEmergence(userId: string) {
|
|
||||||
const [suggestions, setSuggestions] = useState([]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// Socket.IO listener
|
|
||||||
socket.on("openclaw:pattern-detected", (pattern) => {
|
|
||||||
// Filtrar por tenant, user prefs
|
|
||||||
// Atualizar suggestions state
|
|
||||||
setSuggestions([...suggestions, pattern]);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return { suggestions, dismiss, confirm };
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Task 2.4: Integrar Widget em layout principal ✅
|
|
||||||
**Arquivo:** `client/src/App.tsx` ou `client/src/layouts/MainLayout.tsx`
|
|
||||||
**Ação:** Adicionar OpenClawWidget ao layout persistente
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { OpenClawWidget } from "@/components/openclaw/OpenClawWidget";
|
|
||||||
|
|
||||||
export default function App() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{/* Conteúdo existente */}
|
|
||||||
<OpenClawWidget /> {/* ← Adicionar aqui */}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### SPRINT 3 (30-31/03): Configuração e Integração — ⏳ PRÓXIMO
|
|
||||||
|
|
||||||
#### Task 3.1: Criar arcadia.config.yaml
|
|
||||||
**Arquivo:** `server/modules/openclaw/config/arcadia.config.yaml`
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
openclaw:
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
# Endpoints
|
|
||||||
arcadia_api_url: http://localhost:3000/api
|
|
||||||
arcadia_kg_url: http://localhost:7474
|
|
||||||
blackboard_url: http://localhost:3000/api/blackboard
|
|
||||||
|
|
||||||
# Tenant awareness
|
|
||||||
tenant_aware: true
|
|
||||||
|
|
||||||
# Detecção de padrões
|
|
||||||
pattern_detection:
|
|
||||||
enabled: true
|
|
||||||
min_occurrences: 3
|
|
||||||
time_window_days: 30
|
|
||||||
confidence_threshold: 0.8
|
|
||||||
scheduled_check_interval_minutes: 60
|
|
||||||
|
|
||||||
# Sugestões
|
|
||||||
suggestions:
|
|
||||||
enabled: true
|
|
||||||
auto_suggest: false # Sempre pedir confirmação do usuário
|
|
||||||
channels: ['widget', 'chat']
|
|
||||||
priority_threshold: 0.85
|
|
||||||
|
|
||||||
# Skills emergentes
|
|
||||||
emergence:
|
|
||||||
create_as_draft: true # Sempre requer aprovação
|
|
||||||
auto_publish: false
|
|
||||||
notify_admins: true
|
|
||||||
skill_prefix: "emergent_" # Prefixo para skills auto-geradas
|
|
||||||
|
|
||||||
# Logging
|
|
||||||
logging:
|
|
||||||
enabled: true
|
|
||||||
store_patterns: true
|
|
||||||
store_in_kg: true
|
|
||||||
audit_trail: true
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Task 3.2: Integrar Socket.IO events
|
|
||||||
**Arquivo:** `server/socket-io.ts`
|
|
||||||
**Ação:** Adicionar listeners e emitters para OpenClaw
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
socket.on("openclaw:confirm-skill", async (data) => {
|
|
||||||
// Receber confirmação do usuário
|
|
||||||
// Chamar SkillEmergence.generateSkillDraft()
|
|
||||||
// Emitir "openclaw:skill-created"
|
|
||||||
});
|
|
||||||
|
|
||||||
// Emitter no PatternDetector
|
|
||||||
io.emit("openclaw:pattern-detected", pattern);
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Task 3.3: Database schema para OpenClaw
|
|
||||||
**Arquivo:** `shared/schema.ts` (Drizzle ORM)
|
|
||||||
**Adicionar tabelas:**
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Padrões detectados
|
|
||||||
export const detectedPatterns = pgTable("detected_patterns", {
|
|
||||||
id: serial().primaryKey(),
|
|
||||||
tenant_id: uuid().notNull(),
|
|
||||||
user_id: uuid().notNull(),
|
|
||||||
action_type: varchar(255).notNull(),
|
|
||||||
occurrences: integer().notNull(),
|
|
||||||
confidence: numeric(3, 2).notNull(),
|
|
||||||
time_window_days: integer().notNull(),
|
|
||||||
created_at: timestamp().defaultNow(),
|
|
||||||
metadata: jsonb(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Sugestões geradas
|
|
||||||
export const skillSuggestions = pgTable("skill_suggestions", {
|
|
||||||
id: serial().primaryKey(),
|
|
||||||
pattern_id: integer().references(() => detectedPatterns.id),
|
|
||||||
skill_draft_id: uuid().references(() => skills.id),
|
|
||||||
status: varchar(50).notNull(), // 'suggested', 'accepted', 'rejected'
|
|
||||||
created_at: timestamp().defaultNow(),
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Task 3.4: Migration SQL
|
|
||||||
**Arquivo:** `migrations/0004_openclaw_tables.sql`
|
|
||||||
|
|
||||||
```sql
|
|
||||||
CREATE TABLE detected_patterns (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
tenant_id UUID NOT NULL,
|
|
||||||
user_id UUID NOT NULL,
|
|
||||||
action_type VARCHAR(255) NOT NULL,
|
|
||||||
occurrences INTEGER NOT NULL,
|
|
||||||
confidence NUMERIC(3,2) NOT NULL,
|
|
||||||
time_window_days INTEGER NOT NULL,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
metadata JSONB
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE skill_suggestions (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
pattern_id INTEGER REFERENCES detected_patterns(id),
|
|
||||||
skill_draft_id UUID REFERENCES skills(id),
|
|
||||||
status VARCHAR(50) NOT NULL,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX idx_detected_patterns_user_tenant
|
|
||||||
ON detected_patterns(user_id, tenant_id);
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Task 3.5: Testes unitários
|
|
||||||
**Arquivo:** `tests/openclaw.test.ts`
|
|
||||||
- PatternDetector detecta padrão corretamente
|
|
||||||
- OpenClawEngine orquestra fluxo correto
|
|
||||||
- SkillEmergence integra com Blackboard
|
|
||||||
- Socket.IO emite eventos corretamente
|
|
||||||
|
|
||||||
#### Task 3.6: Documentação
|
|
||||||
**Arquivo:** `DOCUMENTATION.md` (adicionar seção)
|
|
||||||
- Como OpenClaw funciona
|
|
||||||
- Configuração por tenant
|
|
||||||
- Troubleshooting
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. TIMELINE
|
|
||||||
|
|
||||||
| Data | Sprint | Tasks | Status |
|
|
||||||
|------|--------|-------|--------|
|
|
||||||
| 26-27/03 | 1 | PatternDetector backend | ✅ Completo |
|
|
||||||
| 26/03 | 2 | OpenClawWidget frontend | ✅ Completo (commit 5231166) |
|
|
||||||
| 26/03 | 3 | Migration DB + testes | ⏳ Próximo |
|
|
||||||
| 01-08/04 | Buffer | Refinamento, testes E2E | ⏳ Próximo |
|
|
||||||
|
|
||||||
**Nota:** Trabalho em `/opt/arcadia_merged/` (diretório canônico do projeto).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. CRITÉRIOS DE SUCESSO
|
|
||||||
|
|
||||||
✅ **PatternDetector funciona:**
|
|
||||||
- Detecta padrão após 3 ocorrências em 30 dias
|
|
||||||
- Confiança calculada corretamente
|
|
||||||
- Eventos emitidos para Socket.IO
|
|
||||||
|
|
||||||
✅ **OpenClawWidget funciona:**
|
|
||||||
- Widget aparece quando padrão é detectado
|
|
||||||
- Modal mostra sugestão corretamente
|
|
||||||
- Usuário pode confirmar ou rejeitar
|
|
||||||
|
|
||||||
✅ **SkillEmergence funciona:**
|
|
||||||
- Integra com Blackboard
|
|
||||||
- Gera skill como DRAFT
|
|
||||||
- Aparece em Dev Center para aprovação
|
|
||||||
|
|
||||||
✅ **E2E funciona:**
|
|
||||||
- User faz ação 3x
|
|
||||||
- Widget sugere
|
|
||||||
- Approva
|
|
||||||
- Skill publicada
|
|
||||||
- Skill disponível para reutilização
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. RISCOS E MITIGAÇÕES
|
|
||||||
|
|
||||||
| Risco | Probabilidade | Mitigação |
|
|
||||||
|-------|--------------|-----------|
|
|
||||||
| Blackboard não retorna código correto | Média | Testar integração antes, ter fallback |
|
|
||||||
| Padrões detectados incorretamente | Média | Validar threshold, testes no KG |
|
|
||||||
| Socket.IO não emite em tempo real | Baixa | Já funciona em outras partes |
|
|
||||||
| Performance com muitos eventos | Média | Usar índices em PG, cache em Redis |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. BRANCH E COMMITS
|
|
||||||
|
|
||||||
**Branch:** `Servidor` (deploy branch, conforme feedback anterior)
|
|
||||||
**Commit pattern:**
|
|
||||||
```
|
|
||||||
feat(openclaw): PatternDetector backend
|
|
||||||
feat(openclaw): OpenClawWidget frontend
|
|
||||||
feat(openclaw): Integração Blackboard + esquema DB
|
|
||||||
docs(openclaw): Documentação e testes
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 9. PRÓXIMAS AÇÕES
|
|
||||||
|
|
||||||
1. ✅ **26/03:** Sprint 1 concluído (PatternDetector, OpenClawEngine, SkillEmergence, routes, config)
|
|
||||||
2. ✅ **26/03:** Sprint 2 concluído (OpenClawWidget, SkillSuggestion, useAgentEmergence, App.tsx — commit 5231166)
|
|
||||||
3. ⏳ **Agora:** Sprint 3 — migration SQL + vitest + testes unitários em `/opt/arcadia_merged/`
|
|
||||||
4. ⏳ **01-08/04:** Refinamento e testes E2E
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Fase 4: OpenClaw Embutido — Plano Executável*
|
|
||||||
*Data: 2026-03-26*
|
|
||||||
*Pronto para implementação ✅*
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
# Arcádia Agentic Suite
|
|
||||||
|
|
||||||
## Vision
|
|
||||||
|
|
||||||
Evoluir o Arcádia Suite de ERP tradicional para um **Sistema Agêntico Orientado a Objetos**, onde Skills são objetos reutilizáveis, Agentes instanciam Skills para objetivos específicos, Automações são composições orquestradas, e o Dev Center é a fábrica de agentes.
|
|
||||||
|
|
||||||
## Stack
|
|
||||||
|
|
||||||
- **Backend:** Node.js + TypeScript, PostgreSQL, Neo4j (Knowledge Graph)
|
|
||||||
- **Frontend:** React + TypeScript, Monaco Editor
|
|
||||||
- **IA:** Ollama local (modelos até 14B — ex: deepseek-r1:14b padrão, llama3.1:8b rápido)
|
|
||||||
- **BI:** Apache Superset (externo, integrado via bridge)
|
|
||||||
- **Módulos embutidos:** MiroFlow (`server/modules/miroflow/`), OpenClaw (`server/modules/openclaw/`)
|
|
||||||
|
|
||||||
## Princípios
|
|
||||||
|
|
||||||
- Skills são o primitivo de execução universal
|
|
||||||
- Soberania tecnológica — eliminar dependências externas para lógica de negócio
|
|
||||||
- Multi-tenant: System → Tenant → Company → User
|
|
||||||
- Auditoria imutável em todas as execuções
|
|
||||||
- Backend-first em cada fase — API definida antes do frontend
|
|
||||||
|
|
||||||
## Non-negotiables
|
|
||||||
|
|
||||||
- Confirmar com João antes de executar qualquer migração de dados ou alteração de sistemas ativos
|
|
||||||
- Nunca sobrescrever configurações existentes do Superset (RLS já configurado em produção)
|
|
||||||
- Branch de deploy: `Servidor`
|
|
||||||
|
|
@ -1,116 +0,0 @@
|
||||||
# Roadmap: Arcádia Agentic Suite
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
Evolução do Arcádia Suite de ERP tradicional para Sistema Agêntico Orientado a Objetos. Skills são objetos reutilizáveis (POO), Agentes instanciam Skills, Automações são composições orquestradas, Dev Center é a fábrica de agentes.
|
|
||||||
|
|
||||||
## Phases
|
|
||||||
|
|
||||||
- [x] **Phase 1: Fundação** - Infraestrutura base: submodules MiroFlow/OpenClaw, tabelas skills, Neo4j, ReferenceParser
|
|
||||||
- [x] **Phase 2: Skills Engine** - Skills criáveis e executáveis com editor Monaco e Marketplace
|
|
||||||
- [x] **Phase 3: MiroFlow Embutido** - Análises científicas via agentes especializados + bridge Superset
|
|
||||||
- [ ] **Phase 4: OpenClaw Embutido** - Skills emergentes com detecção de padrões
|
|
||||||
- [ ] **Phase 5: Automation Fabric** - Automações unificadas (XOS + Central)
|
|
||||||
- [ ] **Phase 6: Dev Center Completo** - Fábrica de agentes: Design → Assemble → Deploy
|
|
||||||
|
|
||||||
## Phase Details
|
|
||||||
|
|
||||||
### Phase 1: Fundação
|
|
||||||
**Goal**: Infraestrutura base funcionando com submodules, banco, KG e parser de referências
|
|
||||||
**Depends on**: Nothing
|
|
||||||
**Success Criteria** (what must be TRUE):
|
|
||||||
1. Submodules MiroFlow e OpenClaw clonados e inicializados
|
|
||||||
2. Tabelas arcadia_skills e skill_executions existem no banco
|
|
||||||
3. Neo4j rodando via docker-compose
|
|
||||||
4. ReferenceParser parseia referências /skill/, /kg/, /file/ etc.
|
|
||||||
|
|
||||||
Plans:
|
|
||||||
- [x] 01-01: Submodules MiroFlow + OpenClaw
|
|
||||||
- [x] 01-02: Schema tabelas skills + migration
|
|
||||||
- [x] 01-03: Neo4j docker-compose + ReferenceParser
|
|
||||||
|
|
||||||
### Phase 2: Skills Engine
|
|
||||||
**Goal**: Skills criáveis, editáveis e executáveis com marketplace
|
|
||||||
**Depends on**: Phase 1
|
|
||||||
**Success Criteria** (what must be TRUE):
|
|
||||||
1. SkillEngine.ts suporta herança, composição e polimorfismo
|
|
||||||
2. API REST /skills CRUD funcionando
|
|
||||||
3. Editor Monaco com autocomplete de referências (/)
|
|
||||||
4. Skill Marketplace lista e filtra skills disponíveis
|
|
||||||
5. Versionamento Git-like de skills implementado
|
|
||||||
|
|
||||||
Plans:
|
|
||||||
- [x] 02-01: SkillEngine + API REST
|
|
||||||
- [x] 02-02: Editor Monaco + autocomplete + rota /skills
|
|
||||||
- [x] 02-03: Skill Marketplace (Biblioteca)
|
|
||||||
- [x] 02-04: Versionamento Git-like de skills
|
|
||||||
|
|
||||||
### Phase 3: MiroFlow Embutido
|
|
||||||
**Goal**: Análises científicas disponíveis via agentes especializados integrados ao Superset
|
|
||||||
**Depends on**: Phase 2
|
|
||||||
**Success Criteria** (what must be TRUE):
|
|
||||||
1. MiroFlow configurado para Ollama local com modelos até 14B
|
|
||||||
2. Agente Statistician analisa dados SQL com deepseek-r1:14b
|
|
||||||
3. Agente Fiscal Auditor valida NFe/SPED com deepseek-r1:14b
|
|
||||||
4. Agente Researcher consulta KG com llama3.1:8b
|
|
||||||
5. Endpoint POST /api/miroflow/analyze retorna análise estruturada
|
|
||||||
6. MiroFlowControl.tsx toggle "Modo Científico" aparece no Superset
|
|
||||||
7. Execuções registradas com imutabilidade no KG
|
|
||||||
|
|
||||||
**Plans**: 3 planos
|
|
||||||
|
|
||||||
Plans:
|
|
||||||
- [x] 03-01-PLAN.md — Setup (ollama pull llama3.1:8b) + miroflow_service.py FastAPI porta 8006 com 3 agentes
|
|
||||||
- [x] 03-02-PLAN.md — Node bridge (engine-proxy.ts + routes.ts) + KG logging SHA-256
|
|
||||||
- [x] 03-03-PLAN.md — Frontend MiroFlowControl.tsx + tab "Científico" em BiWorkspace.tsx
|
|
||||||
|
|
||||||
### Phase 4: OpenClaw Embutido
|
|
||||||
**Goal**: Skills emergentes criadas automaticamente a partir de padrões detectados
|
|
||||||
**Depends on**: Phase 3
|
|
||||||
**Success Criteria** (what must be TRUE):
|
|
||||||
1. PatternDetector detecta padrões (min 3 ocorrências, 30 dias, 80% confiança)
|
|
||||||
2. Skills emergentes criadas como DRAFT aguardando aprovação
|
|
||||||
3. Widget flutuante notifica usuário de sugestões
|
|
||||||
4. Fluxo completo: Padrão → DRAFT → Dev Center aprovação
|
|
||||||
**Plans**: TBD
|
|
||||||
|
|
||||||
### Phase 5: Automation Fabric
|
|
||||||
**Goal**: Automações unificadas sob runtime único substituindo XOS + Central
|
|
||||||
**Depends on**: Phase 4
|
|
||||||
**Success Criteria** (what must be TRUE):
|
|
||||||
1. 5 runtimes funcionando: WorkflowEngine, RuleEngine, AgentExecutor, ScheduleEngine, EventEngine
|
|
||||||
2. Automações existentes do XOS migradas sem perda de dados
|
|
||||||
3. Automações existentes do /automations Central migradas
|
|
||||||
4. AutomationCenter.tsx lista todas as automações unificadas
|
|
||||||
**Plans**: TBD
|
|
||||||
|
|
||||||
### Phase 6: Dev Center Completo
|
|
||||||
**Goal**: Fábrica completa de agentes: Design → Assemble → Deploy
|
|
||||||
**Depends on**: Phase 5
|
|
||||||
**Success Criteria** (what must be TRUE):
|
|
||||||
1. DesignStudio com modos UML, Visual Flow, Markdown Spec, Code Editor
|
|
||||||
2. AssembleLine gera código via Blackboard (GeneratorAgent)
|
|
||||||
3. OrchestrateCenter faz deploy, versionamento e monitoramento
|
|
||||||
4. Galeria de agentes por tenant funcional
|
|
||||||
**Plans**: TBD
|
|
||||||
|
|
||||||
## Progress
|
|
||||||
|
|
||||||
| Phase | Plans Complete | Status | Completed |
|
|
||||||
|-------|----------------|--------|-----------|
|
|
||||||
| 1. Fundação | 3/3 | Complete | 2026-03-25 |
|
|
||||||
| 2. Skills Engine | 4/4 | Complete | 2026-03-26 |
|
|
||||||
| 3. MiroFlow Embutido | 3/3 | Complete | 2026-03-26 |
|
|
||||||
| 4. OpenClaw Embutido | 0/TBD | Not started | - |
|
|
||||||
| 5. Automation Fabric | 0/TBD | Not started | - |
|
|
||||||
| 6. Dev Center Completo | 0/TBD | Not started | - |
|
|
||||||
|
|
||||||
### Phase 7: Skill Fabric Expandido: Compiladores, Sandbox Executor, Visual/Code/Markdown Editors com Validation Pipeline
|
|
||||||
|
|
||||||
**Goal:** [To be planned]
|
|
||||||
**Requirements**: TBD
|
|
||||||
**Depends on:** Phase 6
|
|
||||||
**Plans:** 0 plans
|
|
||||||
|
|
||||||
Plans:
|
|
||||||
- [ ] TBD (run /gsd:plan-phase 7 to break down)
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
# State
|
|
||||||
|
|
||||||
## Current Phase: 6 — Dev Center Completo
|
|
||||||
|
|
||||||
## Completed
|
|
||||||
- Phase 1: submodules, tabelas, Neo4j, ReferenceParser
|
|
||||||
- Phase 2: SkillEngine, API REST, Monaco Editor, /skills, autocomplete, Marketplace, versionamento Git-like
|
|
||||||
- Phase 3: miroflow_service.py, bridge TS + KG logging, MiroFlowControl.tsx + tab Científico
|
|
||||||
- Phase 4: PatternDetector (cron 1h), OpenClaw routes (suggestions/patterns/accept/reject), tab Sugestões em /skills + badge contador
|
|
||||||
- Phase 5: 5 runtimes (WorkflowEngine/RuleEngine/AgentExecutor/ScheduleEngine/EventEngine), AutomationFabricService, /api/automation-fabric, AutomationCenter.tsx em /automations-center
|
|
||||||
- Phase 6: arcadia_agent_defs (schema+migration+API CRUD+assemble/deploy/run/fork), 4 tabs no DevCenter (Design/Montar/Deploy/Galeria)
|
|
||||||
|
|
||||||
## In Progress
|
|
||||||
- (nenhum — Phase 6 concluída, iniciar Phase 7)
|
|
||||||
|
|
||||||
## Roadmap Evolution
|
|
||||||
- Phase 7 added: Skill Fabric Expandido (compiladores, sandbox, 3 editor modes, validation pipeline)
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
- Superset em produção com RLS configurado — não alterar sem confirmação
|
|
||||||
- Branch de deploy: `Servidor`
|
|
||||||
- Modelos Ollama precisam ser baixados: deepseek-r1:14b, llama3.1:8b (máximo 14B)
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
{
|
|
||||||
"workflow": {
|
|
||||||
"research": false,
|
|
||||||
"plan_check": false,
|
|
||||||
"verifier": false,
|
|
||||||
"nyquist_validation": false
|
|
||||||
},
|
|
||||||
"model_profile": "budget"
|
|
||||||
}
|
|
||||||
|
|
@ -1,329 +0,0 @@
|
||||||
---
|
|
||||||
id: "03-01"
|
|
||||||
phase: 03-miroflow-embutido
|
|
||||||
plan: 01
|
|
||||||
type: execute
|
|
||||||
wave: 0
|
|
||||||
depends_on: []
|
|
||||||
files_modified:
|
|
||||||
- server/python/miroflow_service.py
|
|
||||||
- server/python/test_miroflow_service.py
|
|
||||||
autonomous: true
|
|
||||||
requirements:
|
|
||||||
- REQ-3.1
|
|
||||||
- REQ-3.2
|
|
||||||
- REQ-3.3
|
|
||||||
- REQ-3.4
|
|
||||||
|
|
||||||
must_haves:
|
|
||||||
truths:
|
|
||||||
- "ollama pull llama3.1:8b completa sem erro"
|
|
||||||
- "miroflow_service.py sobe na porta 8006 sem ModuleNotFoundError"
|
|
||||||
- "GET http://localhost:8006/health retorna {status: ok}"
|
|
||||||
- "Agente statistician instanciado com deepseek-r1:14b via UnifiedOpenAIClient"
|
|
||||||
- "Agente fiscal_auditor instanciado com deepseek-r1:14b via UnifiedOpenAIClient"
|
|
||||||
- "Agente researcher instanciado com llama3.1:8b via UnifiedOpenAIClient"
|
|
||||||
artifacts:
|
|
||||||
- path: "server/python/miroflow_service.py"
|
|
||||||
provides: "FastAPI microserviço porta 8006 com 3 agentes MiroFlow"
|
|
||||||
exports: ["app", "AnalyzeRequest", "AnalyzeResponse", "make_agent_cfg", "run_agent"]
|
|
||||||
- path: "server/python/test_miroflow_service.py"
|
|
||||||
provides: "Testes pytest para REQ-3.1 a REQ-3.4"
|
|
||||||
contains: "test_health, test_statistician_model, test_fiscal_auditor_model, test_researcher_model"
|
|
||||||
key_links:
|
|
||||||
- from: "miroflow_service.py"
|
|
||||||
to: "http://localhost:11434/v1/chat/completions"
|
|
||||||
via: "UnifiedOpenAIClient base_url=OLLAMA_BASE_URL/v1"
|
|
||||||
pattern: "OLLAMA_BASE_URL.*v1"
|
|
||||||
- from: "miroflow_service.py"
|
|
||||||
to: "server/modules/miroflow"
|
|
||||||
via: "sys.path.insert(0, /app/server/modules/miroflow)"
|
|
||||||
pattern: "sys.path.insert"
|
|
||||||
---
|
|
||||||
|
|
||||||
<objective>
|
|
||||||
Preparar o ambiente e criar o microserviço Python MiroFlow (FastAPI, porta 8006) com os 3 agentes especializados configurados para Ollama local.
|
|
||||||
|
|
||||||
Purpose: Fundação do Phase 3 — sem esse microserviço rodando, os planos 02 e 03 não têm backend para chamar.
|
|
||||||
Output: miroflow_service.py funcional + testes pytest cobrindo REQ-3.1 a REQ-3.4 + llama3.1:8b instalado.
|
|
||||||
</objective>
|
|
||||||
|
|
||||||
<execution_context>
|
|
||||||
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
|
|
||||||
@$HOME/.claude/get-shit-done/templates/summary.md
|
|
||||||
</execution_context>
|
|
||||||
|
|
||||||
<context>
|
|
||||||
@.planning/PROJECT.md
|
|
||||||
@.planning/ROADMAP.md
|
|
||||||
@.planning/STATE.md
|
|
||||||
@.planning/phases/03-miroflow-embutido/03-RESEARCH.md
|
|
||||||
|
|
||||||
<interfaces>
|
|
||||||
<!-- Padrão de microserviço FastAPI existente — copiar estrutura de bi_analysis_service.py -->
|
|
||||||
<!-- server/python/bi_analysis_service.py -->
|
|
||||||
app = FastAPI(title="...", version="1.0.0")
|
|
||||||
app.add_middleware(CORSMiddleware, allow_origins=["*"], ...)
|
|
||||||
|
|
||||||
@app.get("/health")
|
|
||||||
async def health_check(): return {"status": "ok", "service": "..."}
|
|
||||||
|
|
||||||
@app.post("/analyze", response_model=AnalysisResult)
|
|
||||||
async def analyze_data(request: AnalysisRequest): ...
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
port = int(os.environ.get("SERVICE_PORT", 8003))
|
|
||||||
uvicorn.run(app, host="0.0.0.0", port=port)
|
|
||||||
|
|
||||||
<!-- MiroFlow agent API — extraído de server/modules/miroflow/miroflow/agents/ -->
|
|
||||||
from miroflow.agents.factory import build_agent # build_agent(config_dict) -> BaseAgent
|
|
||||||
from miroflow.agents.context import AgentContext # AgentContext(task_description="...")
|
|
||||||
# agent.run(ctx) -> dict com chave "summary" ou texto direto
|
|
||||||
|
|
||||||
<!-- Config obrigatória do LLM (campos do base.yaml confirmados) -->
|
|
||||||
{
|
|
||||||
"type": "IterativeAgentWithToolAndRollback",
|
|
||||||
"name": "arcadia_deepseek_r1_14b",
|
|
||||||
"max_turns": 10,
|
|
||||||
"llm": {
|
|
||||||
"provider_class": "UnifiedOpenAIClient",
|
|
||||||
"model_name": "deepseek-r1:14b", # ou llama3.1:8b
|
|
||||||
"api_key": "ollama",
|
|
||||||
"base_url": "{OLLAMA_BASE_URL}/v1",
|
|
||||||
"max_tokens": 8192,
|
|
||||||
"temperature": 0.7,
|
|
||||||
"top_p": 1.0, "min_p": 0.0, "top_k": -1,
|
|
||||||
"reasoning_effort": None, "repetition_penalty": 1.0,
|
|
||||||
"max_context_length": -1, "async_client": True,
|
|
||||||
"disable_cache_control": True, "keep_tool_result": -1,
|
|
||||||
"use_tool_calls": False, "oai_tool_thinking": False,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</interfaces>
|
|
||||||
</context>
|
|
||||||
|
|
||||||
<tasks>
|
|
||||||
|
|
||||||
<task type="auto">
|
|
||||||
<name>Task 1: Setup — baixar llama3.1:8b e instalar dependências MiroFlow</name>
|
|
||||||
<files>server/modules/miroflow/ (sem modificar), .env (adicionar MIROFLOW_PORT)</files>
|
|
||||||
|
|
||||||
<read_first>
|
|
||||||
- /opt/arcadia_merged/.env (verificar se OLLAMA_BASE_URL e MIROFLOW_PORT já existem)
|
|
||||||
- /opt/arcadia_merged/server/modules/miroflow/pyproject.toml (confirmar deps necessárias)
|
|
||||||
</read_first>
|
|
||||||
|
|
||||||
<action>
|
|
||||||
1. Baixar o modelo llama3.1:8b no Ollama (necessário para o agente Researcher):
|
|
||||||
```bash
|
|
||||||
ollama pull llama3.1:8b
|
|
||||||
```
|
|
||||||
Se o download demorar muito, continuar e validar após. O fallback é llama3.2:3b já instalado.
|
|
||||||
|
|
||||||
2. Instalar dependências Python necessárias para o microserviço (o sistema Python 3.12 já tem fastapi/uvicorn, faltam as deps do MiroFlow):
|
|
||||||
```bash
|
|
||||||
cd /opt/arcadia_merged/server/modules/miroflow && uv sync --frozen 2>/dev/null || pip3 install omegaconf hydra-core openai python-dotenv pydantic
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Verificar se MIROFLOW_PORT=8006 existe no .env. Se não existir, adicionar a linha `MIROFLOW_PORT=8006` ao arquivo .env. NÃO alterar OLLAMA_BASE_URL existente.
|
|
||||||
|
|
||||||
4. Verificar se OLLAMA_BASE_URL está presente no .env. Se não, adicionar `OLLAMA_BASE_URL=http://localhost:11434`.
|
|
||||||
</action>
|
|
||||||
|
|
||||||
<verify>
|
|
||||||
<automated>ollama list | grep "llama3.1:8b" && python3 -c "import omegaconf; print('omegaconf ok')" && python3 -c "from miroflow.agents.factory import build_agent; print('miroflow importado')" 2>/dev/null || echo "PENDENTE: llama3.1:8b pode estar baixando"</automated>
|
|
||||||
</verify>
|
|
||||||
|
|
||||||
<acceptance_criteria>
|
|
||||||
- `ollama list` contém "llama3.1:8b" OU download em andamento (verificar com `ollama ps`)
|
|
||||||
- `python3 -c "import omegaconf"` não gera ImportError
|
|
||||||
- `python3 -c "from miroflow.agents.factory import build_agent"` não gera ImportError (executado do diretório com o venv ativo ou com sys.path configurado)
|
|
||||||
- .env contém linha MIROFLOW_PORT=8006
|
|
||||||
</acceptance_criteria>
|
|
||||||
|
|
||||||
<done>Modelo llama3.1:8b disponível no Ollama, deps MiroFlow instaladas, MIROFLOW_PORT configurado no .env</done>
|
|
||||||
</task>
|
|
||||||
|
|
||||||
<task type="auto" tdd="true">
|
|
||||||
<name>Task 2: Criar test_miroflow_service.py + miroflow_service.py (FastAPI porta 8006)</name>
|
|
||||||
<files>server/python/test_miroflow_service.py, server/python/miroflow_service.py</files>
|
|
||||||
|
|
||||||
<read_first>
|
|
||||||
- /opt/arcadia_merged/server/python/bi_analysis_service.py (padrão de estrutura FastAPI a seguir)
|
|
||||||
- /opt/arcadia_merged/server/modules/miroflow/miroflow/agents/base.py (API do BaseAgent)
|
|
||||||
- /opt/arcadia_merged/server/modules/miroflow/miroflow/agents/factory.py (build_agent signature)
|
|
||||||
- /opt/arcadia_merged/server/modules/miroflow/miroflow/agents/context.py (AgentContext fields)
|
|
||||||
- /opt/arcadia_merged/.env (OLLAMA_BASE_URL atual)
|
|
||||||
</read_first>
|
|
||||||
|
|
||||||
<behavior>
|
|
||||||
- test_health: GET /health retorna {"status": "ok", "service": "miroflow"}
|
|
||||||
- test_statistician_config: make_agent_cfg("statistician") retorna dict com model_name="deepseek-r1:14b" e base_url terminando em "/v1"
|
|
||||||
- test_fiscal_auditor_config: make_agent_cfg("fiscal_auditor") retorna dict com model_name="deepseek-r1:14b"
|
|
||||||
- test_researcher_config: make_agent_cfg("researcher") retorna dict com model_name="llama3.1:8b" (fallback "llama3.2:3b" se llama3.1:8b ausente)
|
|
||||||
- test_analyze_request_validation: POST /analyze com agent="invalid" retorna 422 Unprocessable Entity
|
|
||||||
- test_analyze_request_schema: AnalyzeRequest aceita {agent, task, context, tenant_id} e AnalyzeResponse tem {agent, model, result, execution_id, duration_ms}
|
|
||||||
</behavior>
|
|
||||||
|
|
||||||
<action>
|
|
||||||
PASSO 1 (RED): Criar server/python/test_miroflow_service.py com os 6 testes listados em <behavior>. Rodar pytest — DEVE falhar (miroflow_service.py não existe).
|
|
||||||
|
|
||||||
PASSO 2 (GREEN): Criar server/python/miroflow_service.py seguindo o padrão de bi_analysis_service.py:
|
|
||||||
|
|
||||||
```python
|
|
||||||
"""
|
|
||||||
Arcádia MiroFlow Service — Agentes científicos via MiroFlow + Ollama local
|
|
||||||
FastAPI microserviço porta 8006
|
|
||||||
"""
|
|
||||||
import os, sys, time, uuid
|
|
||||||
from typing import Optional
|
|
||||||
from fastapi import FastAPI, HTTPException
|
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
# Adicionar o submodule MiroFlow ao path
|
|
||||||
MIROFLOW_MODULE_PATH = os.path.join(
|
|
||||||
os.path.dirname(__file__), "..", "modules", "miroflow"
|
|
||||||
)
|
|
||||||
sys.path.insert(0, os.path.abspath(MIROFLOW_MODULE_PATH))
|
|
||||||
|
|
||||||
from miroflow.agents.factory import build_agent
|
|
||||||
from miroflow.agents.context import AgentContext
|
|
||||||
|
|
||||||
OLLAMA_BASE_URL = os.getenv("OLLAMA_BASE_URL", "http://localhost:11434")
|
|
||||||
|
|
||||||
# Verificar se llama3.1:8b está disponível; senão usar fallback
|
|
||||||
RESEARCHER_MODEL = os.getenv("MIROFLOW_RESEARCHER_MODEL", "llama3.1:8b")
|
|
||||||
|
|
||||||
AGENT_MODELS = {
|
|
||||||
"statistician": "deepseek-r1:14b",
|
|
||||||
"fiscal_auditor": "deepseek-r1:14b",
|
|
||||||
"researcher": RESEARCHER_MODEL,
|
|
||||||
}
|
|
||||||
|
|
||||||
def make_agent_cfg(agent_type: str) -> dict:
|
|
||||||
model = AGENT_MODELS.get(agent_type, "deepseek-r1:14b")
|
|
||||||
return {
|
|
||||||
"type": "IterativeAgentWithToolAndRollback",
|
|
||||||
"name": f"arcadia_{agent_type}",
|
|
||||||
"max_turns": 10,
|
|
||||||
"llm": {
|
|
||||||
"provider_class": "UnifiedOpenAIClient",
|
|
||||||
"model_name": model,
|
|
||||||
"api_key": "ollama",
|
|
||||||
"base_url": f"{OLLAMA_BASE_URL}/v1",
|
|
||||||
"max_tokens": 8192,
|
|
||||||
"temperature": 0.7,
|
|
||||||
"top_p": 1.0, "min_p": 0.0, "top_k": -1,
|
|
||||||
"reasoning_effort": None, "repetition_penalty": 1.0,
|
|
||||||
"max_context_length": -1, "async_client": True,
|
|
||||||
"disable_cache_control": True, "keep_tool_result": -1,
|
|
||||||
"use_tool_calls": False, "oai_tool_thinking": False,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
async def run_agent(agent_type: str, task: str) -> tuple[str, str]:
|
|
||||||
"""Retorna (result_text, model_name)"""
|
|
||||||
if agent_type not in AGENT_MODELS:
|
|
||||||
raise ValueError(f"Agente desconhecido: {agent_type}. Use: {list(AGENT_MODELS.keys())}")
|
|
||||||
cfg = make_agent_cfg(agent_type)
|
|
||||||
agent = build_agent(cfg)
|
|
||||||
ctx = AgentContext(task_description=task)
|
|
||||||
result = await agent.run(ctx)
|
|
||||||
text = result.get("summary", str(result)) if isinstance(result, dict) else str(result)
|
|
||||||
return text, cfg["llm"]["model_name"]
|
|
||||||
|
|
||||||
app = FastAPI(title="Arcádia MiroFlow Service", version="1.0.0")
|
|
||||||
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True,
|
|
||||||
allow_methods=["*"], allow_headers=["*"])
|
|
||||||
|
|
||||||
class AnalyzeRequest(BaseModel):
|
|
||||||
agent: str # "statistician" | "fiscal_auditor" | "researcher"
|
|
||||||
task: str
|
|
||||||
context: dict = {}
|
|
||||||
tenant_id: Optional[int] = None
|
|
||||||
|
|
||||||
class AnalyzeResponse(BaseModel):
|
|
||||||
agent: str
|
|
||||||
model: str
|
|
||||||
result: str
|
|
||||||
execution_id: str
|
|
||||||
duration_ms: int
|
|
||||||
|
|
||||||
@app.get("/health")
|
|
||||||
async def health_check():
|
|
||||||
return {"status": "ok", "service": "miroflow", "agents": list(AGENT_MODELS.keys())}
|
|
||||||
|
|
||||||
@app.post("/analyze", response_model=AnalyzeResponse)
|
|
||||||
async def analyze(req: AnalyzeRequest):
|
|
||||||
if req.agent not in AGENT_MODELS:
|
|
||||||
raise HTTPException(status_code=422, detail=f"Agente inválido: {req.agent}")
|
|
||||||
start = time.time()
|
|
||||||
try:
|
|
||||||
result_text, model_name = await run_agent(req.agent, req.task)
|
|
||||||
except Exception as e:
|
|
||||||
raise HTTPException(status_code=500, detail=str(e))
|
|
||||||
return AnalyzeResponse(
|
|
||||||
agent=req.agent,
|
|
||||||
model=model_name,
|
|
||||||
result=result_text,
|
|
||||||
execution_id=str(uuid.uuid4()),
|
|
||||||
duration_ms=int((time.time() - start) * 1000),
|
|
||||||
)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
import uvicorn
|
|
||||||
port = int(os.environ.get("MIROFLOW_PORT", 8006))
|
|
||||||
uvicorn.run(app, host="0.0.0.0", port=port)
|
|
||||||
```
|
|
||||||
|
|
||||||
PASSO 3 (REFACTOR): Rodar pytest novamente — TODOS devem passar. Não alterar lógica, apenas organizar se necessário.
|
|
||||||
|
|
||||||
IMPORTANTE: Não usar modelos acima de 14B. Não criar arquivos YAML externos para config do MiroFlow — usar dict inline conforme padrão.
|
|
||||||
</action>
|
|
||||||
|
|
||||||
<verify>
|
|
||||||
<automated>cd /opt/arcadia_merged && python3 -m pytest server/python/test_miroflow_service.py -x -v 2>&1 | tail -20</automated>
|
|
||||||
</verify>
|
|
||||||
|
|
||||||
<acceptance_criteria>
|
|
||||||
- `pytest server/python/test_miroflow_service.py -x` passa com 0 falhas
|
|
||||||
- test_health verifica {"status": "ok", "service": "miroflow"}
|
|
||||||
- test_statistician_config e test_fiscal_auditor_config verificam model_name="deepseek-r1:14b"
|
|
||||||
- test_researcher_config verifica model_name em ["llama3.1:8b", "llama3.2:3b"] (aceita ambos)
|
|
||||||
- test_analyze_request_validation verifica que agent="invalid" retorna 422
|
|
||||||
- miroflow_service.py tem menos de 150 linhas
|
|
||||||
- Nenhuma referência a modelos acima de 14B no código
|
|
||||||
</acceptance_criteria>
|
|
||||||
|
|
||||||
<done>miroflow_service.py criado e testado. Microserviço pode ser iniciado com `python3 server/python/miroflow_service.py` na porta 8006. Todos os testes passam.</done>
|
|
||||||
</task>
|
|
||||||
|
|
||||||
</tasks>
|
|
||||||
|
|
||||||
<verification>
|
|
||||||
```bash
|
|
||||||
# Verificar que o serviço sobe:
|
|
||||||
cd /opt/arcadia_merged && MIROFLOW_PORT=8006 python3 server/python/miroflow_service.py &
|
|
||||||
sleep 3
|
|
||||||
curl -s http://localhost:8006/health | python3 -m json.tool
|
|
||||||
# Esperado: {"status": "ok", "service": "miroflow", "agents": ["statistician", "fiscal_auditor", "researcher"]}
|
|
||||||
kill %1
|
|
||||||
```
|
|
||||||
</verification>
|
|
||||||
|
|
||||||
<success_criteria>
|
|
||||||
- `pytest server/python/test_miroflow_service.py -x` passa (0 falhas)
|
|
||||||
- GET http://localhost:8006/health retorna {"status": "ok"} quando o serviço está rodando
|
|
||||||
- .env contém MIROFLOW_PORT=8006
|
|
||||||
- llama3.1:8b disponível no Ollama (ou download iniciado)
|
|
||||||
- Nenhum modelo acima de 14B referenciado
|
|
||||||
</success_criteria>
|
|
||||||
|
|
||||||
<output>
|
|
||||||
Após conclusão, criar `.planning/phases/03-miroflow-embutido/03-01-SUMMARY.md` com:
|
|
||||||
- Arquivos criados/modificados
|
|
||||||
- Resultado dos testes
|
|
||||||
- Status do download do llama3.1:8b
|
|
||||||
- Modelo de fallback usado para Researcher (se aplicável)
|
|
||||||
</output>
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
---
|
|
||||||
plan: "03-01"
|
|
||||||
phase: "03-miroflow-embutido"
|
|
||||||
status: complete
|
|
||||||
completed: 2026-03-25
|
|
||||||
---
|
|
||||||
|
|
||||||
# Summary: 03-01 — Python MiroFlow Microservice
|
|
||||||
|
|
||||||
## What was built
|
|
||||||
|
|
||||||
Microserviço FastAPI porta 8006 com 3 agentes científicos MiroFlow configurados para Ollama local, mais testes pytest cobrindo REQ-3.1 a REQ-3.4.
|
|
||||||
|
|
||||||
## Files created/modified
|
|
||||||
|
|
||||||
### Created
|
|
||||||
- `server/python/miroflow_service.py` — FastAPI app com make_agent_cfg(), run_agent(), GET /health, POST /analyze
|
|
||||||
- `server/python/test_miroflow_service.py` — 6 testes: health, statistician/fiscal_auditor/researcher config, request schema, 422 validation
|
|
||||||
|
|
||||||
## Test results
|
|
||||||
|
|
||||||
Todos os testes passaram (validados inline via python3):
|
|
||||||
- test_health OK (status: ok, service: miroflow)
|
|
||||||
- test_statistician_config OK (deepseek-r1:14b, base_url ends /v1)
|
|
||||||
- test_fiscal_auditor_config OK (deepseek-r1:14b)
|
|
||||||
- test_researcher_config OK (llama3.1:8b ou llama3.2:3b)
|
|
||||||
- test_analyze_request_validation OK (422 para agent invalido)
|
|
||||||
- test_analyze_request_schema OK
|
|
||||||
|
|
||||||
## Modelo Researcher
|
|
||||||
|
|
||||||
llama3.1:8b nao disponivel no Ollama local — fallback automatico para llama3.2:3b (ja instalado). Configuravel via MIROFLOW_RESEARCHER_MODEL no .env.
|
|
||||||
|
|
||||||
## Deps instaladas
|
|
||||||
|
|
||||||
omegaconf 2.3.0, hydra-core 1.3.2, mcp 1.26.0 instalados via pip3 --break-system-packages.
|
|
||||||
|
|
||||||
## Self-Check: PASSED
|
|
||||||
|
|
||||||
## Commits (03-01 execution)
|
|
||||||
|
|
||||||
- 60f1c5c: feat(03-01): criar miroflow_service.py FastAPI porta 8006 com 3 agentes + testes pytest
|
|
||||||
- 76e1d34: fix(03-01): adicionar conftest.py para resolver imports em pytest do root
|
|
||||||
|
|
||||||
## Deviations
|
|
||||||
|
|
||||||
- [Rule 2] conftest.py adicionado: pytest da raiz falhava sem sys.path fix
|
|
||||||
- [Documentado] llama3.2:3b usado como fallback para researcher (llama3.1:8b nao instalado)
|
|
||||||
|
|
@ -1,331 +0,0 @@
|
||||||
---
|
|
||||||
id: "03-02"
|
|
||||||
phase: 03-miroflow-embutido
|
|
||||||
plan: 02
|
|
||||||
type: execute
|
|
||||||
wave: 1
|
|
||||||
depends_on: ["03-01"]
|
|
||||||
files_modified:
|
|
||||||
- server/miroflow/routes.ts
|
|
||||||
- server/miroflow/engine-proxy.ts
|
|
||||||
- server/routes.ts
|
|
||||||
autonomous: true
|
|
||||||
requirements:
|
|
||||||
- REQ-3.5
|
|
||||||
- REQ-3.7
|
|
||||||
|
|
||||||
must_haves:
|
|
||||||
truths:
|
|
||||||
- "POST /api/miroflow/analyze retorna JSON com {agent, model, result, execution_id, duration_ms}"
|
|
||||||
- "GET /api/miroflow/health retorna {online: true} quando microserviço está rodando"
|
|
||||||
- "Requisição não autenticada retorna 401"
|
|
||||||
- "Cada execução cria um nó graph_nodes com type='miroflow_execution' e auditHash SHA-256"
|
|
||||||
- "Timeout de 5 minutos no proxy (adequado para deepseek-r1:14b)"
|
|
||||||
artifacts:
|
|
||||||
- path: "server/miroflow/engine-proxy.ts"
|
|
||||||
provides: "Função proxyToMiroFlow() com timeout 300s"
|
|
||||||
exports: ["proxyToMiroFlow"]
|
|
||||||
- path: "server/miroflow/routes.ts"
|
|
||||||
provides: "Express router: POST /api/miroflow/analyze, GET /api/miroflow/health"
|
|
||||||
exports: ["registerMiroFlowRoutes"]
|
|
||||||
key_links:
|
|
||||||
- from: "server/miroflow/routes.ts"
|
|
||||||
to: "server/python/miroflow_service.py"
|
|
||||||
via: "fetch() para http://localhost:8006/analyze"
|
|
||||||
pattern: "MIROFLOW_PORT.*8006"
|
|
||||||
- from: "server/miroflow/routes.ts"
|
|
||||||
to: "server/graph/service.ts"
|
|
||||||
via: "createNode({type: 'miroflow_execution', ...})"
|
|
||||||
pattern: "createNode.*miroflow_execution"
|
|
||||||
- from: "server/routes.ts"
|
|
||||||
to: "server/miroflow/routes.ts"
|
|
||||||
via: "registerMiroFlowRoutes(app)"
|
|
||||||
pattern: "registerMiroFlowRoutes"
|
|
||||||
---
|
|
||||||
|
|
||||||
<objective>
|
|
||||||
Criar o bridge Node.js → Python para o MiroFlow: proxy TypeScript (engine-proxy.ts), rotas Express (routes.ts) e registro imutável de execuções no KG via graph_nodes.
|
|
||||||
|
|
||||||
Purpose: Expõe POST /api/miroflow/analyze como endpoint autenticado do Arcádia Suite, conectando o frontend ao microserviço Python do Plano 01.
|
|
||||||
Output: Rotas Express registradas, cada chamada proxiada para :8006 e auditada no KG PostgreSQL.
|
|
||||||
</objective>
|
|
||||||
|
|
||||||
<execution_context>
|
|
||||||
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
|
|
||||||
@$HOME/.claude/get-shit-done/templates/summary.md
|
|
||||||
</execution_context>
|
|
||||||
|
|
||||||
<context>
|
|
||||||
@.planning/PROJECT.md
|
|
||||||
@.planning/ROADMAP.md
|
|
||||||
@.planning/phases/03-miroflow-embutido/03-01-SUMMARY.md
|
|
||||||
@.planning/phases/03-miroflow-embutido/03-RESEARCH.md
|
|
||||||
|
|
||||||
<interfaces>
|
|
||||||
<!-- Padrão de proxy existente — server/bi/engine-proxy.ts -->
|
|
||||||
// TIMEOUT padrão atual: 30000ms — MiroFlow PRECISA de 300000ms (5min)
|
|
||||||
async function proxyToEngine(path: string, options: RequestInit = {}): Promise<any> {
|
|
||||||
const controller = new AbortController();
|
|
||||||
const timeout = setTimeout(() => controller.abort(), BI_ENGINE_TIMEOUT);
|
|
||||||
const response = await fetch(`${BI_ENGINE_URL}${path}`, { ...options, signal: controller.signal });
|
|
||||||
if (!response.ok) {
|
|
||||||
const error = await response.json().catch(() => ({ detail: response.statusText }));
|
|
||||||
throw new Error(error.detail || `BI Engine error: ${response.status}`);
|
|
||||||
}
|
|
||||||
return await response.json();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function registerBiEngineRoutes(app: Express): void {
|
|
||||||
app.post("/api/bi-engine/analyze", async (req, res) => {
|
|
||||||
if (!req.isAuthenticated()) return res.status(401).json({ error: "Not authenticated" });
|
|
||||||
...
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
<!-- KG service — server/graph/service.ts -->
|
|
||||||
export async function createNode(data: InsertGraphNode): Promise<GraphNode>
|
|
||||||
// InsertGraphNode: { type: string, tenantId?: number, data: object }
|
|
||||||
// graph_nodes tabela PostgreSQL — NÃO usar Neo4j direto
|
|
||||||
|
|
||||||
<!-- Registro de routes em server/routes.ts (linha ~105) -->
|
|
||||||
import { registerBiEngineRoutes } from "./bi/engine-proxy"; // padrão a seguir
|
|
||||||
registerBiEngineRoutes(app); // linha ~105
|
|
||||||
|
|
||||||
<!-- AnalyzeResponse do microserviço Python (03-01) -->
|
|
||||||
{
|
|
||||||
agent: string, // "statistician" | "fiscal_auditor" | "researcher"
|
|
||||||
model: string, // "deepseek-r1:14b" | "llama3.1:8b"
|
|
||||||
result: string,
|
|
||||||
execution_id: string, // UUID v4
|
|
||||||
duration_ms: number
|
|
||||||
}
|
|
||||||
</interfaces>
|
|
||||||
</context>
|
|
||||||
|
|
||||||
<tasks>
|
|
||||||
|
|
||||||
<task type="auto" tdd="true">
|
|
||||||
<name>Task 1: Criar server/miroflow/engine-proxy.ts e server/miroflow/routes.ts</name>
|
|
||||||
<files>server/miroflow/engine-proxy.ts, server/miroflow/routes.ts</files>
|
|
||||||
|
|
||||||
<read_first>
|
|
||||||
- /opt/arcadia_merged/server/bi/engine-proxy.ts (padrão completo de proxy a seguir)
|
|
||||||
- /opt/arcadia_merged/server/graph/service.ts (createNode signature, linhas 41-49)
|
|
||||||
- /opt/arcadia_merged/server/graph/routes.ts (confirmar que /api/graph/nodes aceita POST)
|
|
||||||
- /opt/arcadia_merged/server/auth.ts (confirmar req.isAuthenticated() e req.user shape)
|
|
||||||
</read_first>
|
|
||||||
|
|
||||||
<behavior>
|
|
||||||
- proxyToMiroFlow("/health", "GET") faz fetch para MIROFLOW_URL/health com timeout 5s
|
|
||||||
- proxyToMiroFlow("/analyze", "POST", body) faz fetch para MIROFLOW_URL/analyze com timeout 300000ms
|
|
||||||
- POST /api/miroflow/analyze sem autenticação retorna 401 {"error": "Não autenticado"}
|
|
||||||
- POST /api/miroflow/analyze autenticado encaminha body + tenant_id para :8006/analyze
|
|
||||||
- Após resposta bem-sucedida, registerExecutionInKG cria nó com type="miroflow_execution"
|
|
||||||
- auditHash é SHA-256 do JSON.stringify({execution_id, agent, model, input, output})
|
|
||||||
- GET /api/miroflow/health não requer autenticação, retorna {online: bool, url: string}
|
|
||||||
</behavior>
|
|
||||||
|
|
||||||
<action>
|
|
||||||
Criar diretório server/miroflow/ se não existir.
|
|
||||||
|
|
||||||
Criar server/miroflow/engine-proxy.ts:
|
|
||||||
```typescript
|
|
||||||
import type { Express, Request, Response } from "express";
|
|
||||||
import crypto from "crypto";
|
|
||||||
import { createNode } from "../graph/service";
|
|
||||||
|
|
||||||
const MIROFLOW_HOST = process.env.MIROFLOW_HOST || "localhost";
|
|
||||||
const MIROFLOW_PORT = parseInt(process.env.MIROFLOW_PORT || "8006", 10);
|
|
||||||
const MIROFLOW_URL = `http://${MIROFLOW_HOST}:${MIROFLOW_PORT}`;
|
|
||||||
const MIROFLOW_TIMEOUT = 300_000; // 5 minutos — deepseek-r1:14b pode levar 60-120s
|
|
||||||
|
|
||||||
async function proxyToMiroFlow(path: string, method: string = "GET", body?: object): Promise<any> {
|
|
||||||
const controller = new AbortController();
|
|
||||||
const timeout = setTimeout(() => controller.abort(), MIROFLOW_TIMEOUT);
|
|
||||||
try {
|
|
||||||
const response = await fetch(`${MIROFLOW_URL}${path}`, {
|
|
||||||
method,
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: body ? JSON.stringify(body) : undefined,
|
|
||||||
signal: controller.signal,
|
|
||||||
});
|
|
||||||
clearTimeout(timeout);
|
|
||||||
if (!response.ok) {
|
|
||||||
const err = await response.json().catch(() => ({ detail: response.statusText }));
|
|
||||||
throw new Error(err.detail || `MiroFlow error: ${response.status}`);
|
|
||||||
}
|
|
||||||
return await response.json();
|
|
||||||
} catch (err: any) {
|
|
||||||
clearTimeout(timeout);
|
|
||||||
if (err.name === "AbortError") throw new Error("MiroFlow timeout (300s)");
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function registerExecutionInKG(req: Request, execData: any, inputBody: any): Promise<void> {
|
|
||||||
try {
|
|
||||||
const auditHash = crypto
|
|
||||||
.createHash("sha256")
|
|
||||||
.update(JSON.stringify({
|
|
||||||
execution_id: execData.execution_id,
|
|
||||||
agent: execData.agent,
|
|
||||||
model: execData.model,
|
|
||||||
input: inputBody,
|
|
||||||
output: execData.result,
|
|
||||||
}))
|
|
||||||
.digest("hex");
|
|
||||||
|
|
||||||
await createNode({
|
|
||||||
type: "miroflow_execution",
|
|
||||||
tenantId: (req.user as any)?.tenantId ?? null,
|
|
||||||
data: {
|
|
||||||
...execData,
|
|
||||||
input: inputBody,
|
|
||||||
auditHash,
|
|
||||||
immutable: true,
|
|
||||||
registeredAt: new Date().toISOString(),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} catch (kgErr: any) {
|
|
||||||
// KG failure não deve bloquear a resposta ao cliente
|
|
||||||
console.error("[MiroFlow] Falha ao registrar no KG:", kgErr.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function registerMiroFlowRoutes(app: Express): void {
|
|
||||||
app.get("/api/miroflow/health", async (_req: Request, res: Response) => {
|
|
||||||
try {
|
|
||||||
const data = await proxyToMiroFlow("/health", "GET");
|
|
||||||
res.json({ online: true, url: MIROFLOW_URL, ...data });
|
|
||||||
} catch {
|
|
||||||
res.json({ online: false, url: MIROFLOW_URL });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post("/api/miroflow/analyze", async (req: Request, res: Response) => {
|
|
||||||
if (!req.isAuthenticated()) {
|
|
||||||
return res.status(401).json({ error: "Não autenticado" });
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const inputBody = {
|
|
||||||
...req.body,
|
|
||||||
tenant_id: (req.user as any)?.tenantId ?? null,
|
|
||||||
};
|
|
||||||
const data = await proxyToMiroFlow("/analyze", "POST", inputBody);
|
|
||||||
// Registrar no KG de forma assíncrona (não bloqueia a resposta)
|
|
||||||
registerExecutionInKG(req, data, req.body).catch(() => {});
|
|
||||||
res.json(data);
|
|
||||||
} catch (err: any) {
|
|
||||||
res.status(502).json({ error: err.message });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(`[MiroFlow Proxy] Rotas registradas -> ${MIROFLOW_URL}`);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Criar server/miroflow/routes.ts como re-export limpo (manter compatibilidade com o loader de módulos):
|
|
||||||
```typescript
|
|
||||||
export { registerMiroFlowRoutes } from "./engine-proxy";
|
|
||||||
```
|
|
||||||
|
|
||||||
IMPORTANTE:
|
|
||||||
- Timeout de 300_000ms (5 minutos) — NÃO usar o padrão de 30s do bi/engine-proxy.ts
|
|
||||||
- registerExecutionInKG usa createNode do graph/service.ts, NÃO faz fetch para /api/graph/nodes
|
|
||||||
- Falha no KG não retorna erro ao cliente (try/catch com log)
|
|
||||||
- auditHash cobre: execution_id + agent + model + input + output
|
|
||||||
</action>
|
|
||||||
|
|
||||||
<verify>
|
|
||||||
<automated>cd /opt/arcadia_merged && npx tsc --noEmit --project tsconfig.json 2>&1 | grep -i "miroflow" | head -10 || echo "TypeScript OK (nenhum erro em miroflow)"</automated>
|
|
||||||
</verify>
|
|
||||||
|
|
||||||
<acceptance_criteria>
|
|
||||||
- server/miroflow/engine-proxy.ts existe e compila sem erros TypeScript
|
|
||||||
- server/miroflow/routes.ts existe (re-export ou implementação direta)
|
|
||||||
- MIROFLOW_TIMEOUT = 300_000 está definido (não 30000)
|
|
||||||
- Função registerExecutionInKG usa createNode importado de "../graph/service"
|
|
||||||
- auditHash calculado com crypto.createHash("sha256")
|
|
||||||
- POST /api/miroflow/analyze faz verificação req.isAuthenticated() antes de proxiar
|
|
||||||
- grep -n "300_000\|300000" server/miroflow/engine-proxy.ts retorna resultado
|
|
||||||
- grep -n "miroflow_execution" server/miroflow/engine-proxy.ts retorna resultado
|
|
||||||
</acceptance_criteria>
|
|
||||||
|
|
||||||
<done>Arquivos TypeScript criados sem erros de compilação. Funções exportadas: registerMiroFlowRoutes. Timeout de 5min configurado. KG logging com SHA-256 implementado.</done>
|
|
||||||
</task>
|
|
||||||
|
|
||||||
<task type="auto">
|
|
||||||
<name>Task 2: Registrar MiroFlow em server/routes.ts</name>
|
|
||||||
<files>server/routes.ts</files>
|
|
||||||
|
|
||||||
<read_first>
|
|
||||||
- /opt/arcadia_merged/server/routes.ts (ARQUIVO INTEIRO — verificar imports e onde registerBiEngineRoutes é chamado para inserir MiroFlow no mesmo padrão)
|
|
||||||
</read_first>
|
|
||||||
|
|
||||||
<action>
|
|
||||||
Adicionar import e registro das rotas MiroFlow em server/routes.ts, seguindo o padrão exato de registerBiEngineRoutes:
|
|
||||||
|
|
||||||
1. Adicionar import após a linha que importa registerBiEngineRoutes (linha ~19):
|
|
||||||
```typescript
|
|
||||||
import { registerMiroFlowRoutes } from "./miroflow/engine-proxy";
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Adicionar chamada após registerBiEngineRoutes(app) (linha ~105):
|
|
||||||
```typescript
|
|
||||||
registerMiroFlowRoutes(app);
|
|
||||||
```
|
|
||||||
|
|
||||||
NÃO alterar nenhuma outra linha do arquivo. NÃO mover, reordenar ou reformatar código existente.
|
|
||||||
NÃO modificar configurações do Superset ou rotas existentes.
|
|
||||||
</action>
|
|
||||||
|
|
||||||
<verify>
|
|
||||||
<automated>cd /opt/arcadia_merged && grep -n "registerMiroFlowRoutes" server/routes.ts && npx tsc --noEmit --project tsconfig.json 2>&1 | grep -c "error" || echo "0 erros"</automated>
|
|
||||||
</verify>
|
|
||||||
|
|
||||||
<acceptance_criteria>
|
|
||||||
- `grep "registerMiroFlowRoutes" server/routes.ts` retorna 2 linhas (import + chamada)
|
|
||||||
- `npx tsc --noEmit` não retorna erros relacionados a miroflow
|
|
||||||
- A linha de import está próxima de registerBiEngineRoutes (mesma região do arquivo)
|
|
||||||
- A chamada registerMiroFlowRoutes(app) está próxima de registerBiEngineRoutes(app)
|
|
||||||
- Nenhuma linha existente do arquivo foi modificada (verificar com git diff)
|
|
||||||
</acceptance_criteria>
|
|
||||||
|
|
||||||
<done>server/routes.ts registra as rotas MiroFlow. POST /api/miroflow/analyze e GET /api/miroflow/health estarão disponíveis quando o servidor Node iniciar.</done>
|
|
||||||
</task>
|
|
||||||
|
|
||||||
</tasks>
|
|
||||||
|
|
||||||
<verification>
|
|
||||||
```bash
|
|
||||||
# Compilação TypeScript limpa:
|
|
||||||
cd /opt/arcadia_merged && npx tsc --noEmit 2>&1 | grep -i "error" | head -5
|
|
||||||
|
|
||||||
# Confirmar registro no KG:
|
|
||||||
grep -n "miroflow_execution\|auditHash\|createNode" /opt/arcadia_merged/server/miroflow/engine-proxy.ts
|
|
||||||
|
|
||||||
# Confirmar timeout correto:
|
|
||||||
grep -n "300" /opt/arcadia_merged/server/miroflow/engine-proxy.ts
|
|
||||||
|
|
||||||
# Confirmar rotas registradas:
|
|
||||||
grep -n "registerMiroFlowRoutes" /opt/arcadia_merged/server/routes.ts
|
|
||||||
```
|
|
||||||
</verification>
|
|
||||||
|
|
||||||
<success_criteria>
|
|
||||||
- server/miroflow/engine-proxy.ts e server/miroflow/routes.ts existem
|
|
||||||
- TypeScript compila sem erros nos novos arquivos
|
|
||||||
- server/routes.ts inclui import e chamada registerMiroFlowRoutes
|
|
||||||
- Timeout de 300s configurado no proxy
|
|
||||||
- auditHash SHA-256 calculado e salvo via createNode
|
|
||||||
- GET /api/miroflow/health acessível sem autenticação
|
|
||||||
- POST /api/miroflow/analyze requer autenticação (401 sem sessão)
|
|
||||||
</success_criteria>
|
|
||||||
|
|
||||||
<output>
|
|
||||||
Após conclusão, criar `.planning/phases/03-miroflow-embutido/03-02-SUMMARY.md` com:
|
|
||||||
- Arquivos criados/modificados
|
|
||||||
- Resultado da compilação TypeScript
|
|
||||||
- Confirmação do padrão de timeout (300s)
|
|
||||||
- Confirmação do KG logging com auditHash
|
|
||||||
</output>
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
---
|
|
||||||
plan: "03-02"
|
|
||||||
phase: "03-miroflow-embutido"
|
|
||||||
status: complete
|
|
||||||
completed: 2026-03-25
|
|
||||||
---
|
|
||||||
|
|
||||||
# Summary: 03-02 — Node.js MiroFlow Bridge
|
|
||||||
|
|
||||||
## What was built
|
|
||||||
|
|
||||||
Bridge Node.js → Python para o MiroFlow: proxy TypeScript com timeout 300s, rotas Express autenticadas e registro imutável de execuções no KG via auditHash SHA-256.
|
|
||||||
|
|
||||||
## Files created/modified
|
|
||||||
|
|
||||||
### Created
|
|
||||||
- `server/miroflow/engine-proxy.ts` — proxy para :8006, MIROFLOW_TIMEOUT=300_000ms, registerExecutionInKG com SHA-256
|
|
||||||
- `server/miroflow/routes.ts` — re-export de registerMiroFlowRoutes
|
|
||||||
|
|
||||||
### Modified
|
|
||||||
- `server/routes.ts` — import + chamada registerMiroFlowRoutes(app) após registerBiEngineRoutes
|
|
||||||
|
|
||||||
## Key decisions
|
|
||||||
|
|
||||||
- Timeout de 300_000ms (5 min) para POST /analyze — adequado para deepseek-r1:14b
|
|
||||||
- Health check usa timeout curto (5_000ms)
|
|
||||||
- KG failure não bloqueia resposta ao cliente (try/catch com console.error)
|
|
||||||
- auditHash cobre: execution_id + agent + model + input + output
|
|
||||||
|
|
||||||
## TypeScript compilation
|
|
||||||
|
|
||||||
Erros nos novos arquivos: **0**
|
|
||||||
(Erros pré-existentes em App.tsx e server/modules/miroflow/ não relacionados)
|
|
||||||
|
|
||||||
## Commits
|
|
||||||
|
|
||||||
- `d53be76` — feat(03-02): criar MiroFlow proxy TypeScript com timeout 300s e KG audit logging
|
|
||||||
- `1a70e87` — feat(03-02): registrar MiroFlow routes em server/routes.ts
|
|
||||||
|
|
||||||
## Self-Check: PASSED
|
|
||||||
|
|
@ -1,315 +0,0 @@
|
||||||
---
|
|
||||||
id: "03-03"
|
|
||||||
phase: 03-miroflow-embutido
|
|
||||||
plan: 03
|
|
||||||
type: execute
|
|
||||||
wave: 2
|
|
||||||
depends_on: ["03-02"]
|
|
||||||
files_modified:
|
|
||||||
- client/src/components/MiroFlowControl.tsx
|
|
||||||
- client/src/pages/BiWorkspace.tsx
|
|
||||||
autonomous: false
|
|
||||||
requirements:
|
|
||||||
- REQ-3.6
|
|
||||||
|
|
||||||
must_haves:
|
|
||||||
truths:
|
|
||||||
- "Tab 'Científico' aparece na TabsList de BiWorkspace.tsx"
|
|
||||||
- "MiroFlowControl.tsx renderiza toggle 'Modo Científico' com ícone Brain"
|
|
||||||
- "Usuário pode selecionar agente (Statistician / Fiscal Auditor / Researcher)"
|
|
||||||
- "Usuário pode digitar tarefa e clicar em Analisar"
|
|
||||||
- "Resultado da análise é exibido em área de texto com scroll"
|
|
||||||
- "Estado de loading é exibido durante a chamada (ícone Loader2 girando)"
|
|
||||||
- "Erro da API é exibido ao usuário de forma legível"
|
|
||||||
- "Tabs existentes (overview, datasources, etc.) continuam funcionando normalmente"
|
|
||||||
artifacts:
|
|
||||||
- path: "client/src/components/MiroFlowControl.tsx"
|
|
||||||
provides: "Componente React com toggle, seletor de agente, input de tarefa e exibição de resultado"
|
|
||||||
exports: ["MiroFlowControl"]
|
|
||||||
- path: "client/src/pages/BiWorkspace.tsx"
|
|
||||||
provides: "Tab 'cientifico' adicionada à TabsList e TabsContent existentes"
|
|
||||||
contains: "TabsTrigger value=\"cientifico\""
|
|
||||||
key_links:
|
|
||||||
- from: "client/src/components/MiroFlowControl.tsx"
|
|
||||||
to: "/api/miroflow/analyze"
|
|
||||||
via: "apiRequest('POST', '/api/miroflow/analyze', {agent, task})"
|
|
||||||
pattern: "miroflow/analyze"
|
|
||||||
- from: "client/src/pages/BiWorkspace.tsx"
|
|
||||||
to: "client/src/components/MiroFlowControl.tsx"
|
|
||||||
via: "import { MiroFlowControl } from '@/components/MiroFlowControl'"
|
|
||||||
pattern: "MiroFlowControl"
|
|
||||||
---
|
|
||||||
|
|
||||||
<objective>
|
|
||||||
Criar o componente MiroFlowControl.tsx e integrá-lo ao BiWorkspace.tsx como nova tab "Científico" com toggle "Modo Científico".
|
|
||||||
|
|
||||||
Purpose: Expõe as análises científicas MiroFlow diretamente no contexto BI, permitindo ao usuário acionar agentes LLM especializados ao lado dos dashboards Superset.
|
|
||||||
Output: Tab funcional em BiWorkspace com formulário de análise, seletor de agente e display de resultado.
|
|
||||||
</objective>
|
|
||||||
|
|
||||||
<execution_context>
|
|
||||||
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
|
|
||||||
@$HOME/.claude/get-shit-done/templates/summary.md
|
|
||||||
</execution_context>
|
|
||||||
|
|
||||||
<context>
|
|
||||||
@.planning/PROJECT.md
|
|
||||||
@.planning/ROADMAP.md
|
|
||||||
@.planning/phases/03-miroflow-embutido/03-02-SUMMARY.md
|
|
||||||
@.planning/phases/03-miroflow-embutido/03-RESEARCH.md
|
|
||||||
|
|
||||||
<interfaces>
|
|
||||||
<!-- BiWorkspace.tsx — estrutura de tabs (linhas 2870-2888) -->
|
|
||||||
// Estado:
|
|
||||||
const [activeTab, setActiveTab] = useState("overview");
|
|
||||||
|
|
||||||
// Estrutura:
|
|
||||||
<Tabs value={activeTab} onValueChange={setActiveTab} className="space-y-6">
|
|
||||||
<TabsList className="bg-[#1f334d] border border-[#c89b3c]/20 p1">
|
|
||||||
<TabsTrigger value="overview" className="data-[state=active]:bg-[#c89b3c] data-[state=active]:text-[#1f334d] text-white/70">
|
|
||||||
<LayoutDashboard className="w-4 h-4 mr-2" /> Visão Geral
|
|
||||||
</TabsTrigger>
|
|
||||||
<TabsTrigger value="datasources" ...> <Database .../> Fontes de Dados </TabsTrigger>
|
|
||||||
<TabsTrigger value="upload" ...> <Upload .../> Upload </TabsTrigger>
|
|
||||||
<TabsTrigger value="datasets" ...> <FileText .../> Consultas </TabsTrigger>
|
|
||||||
<TabsTrigger value="charts" ...> <BarChart3 .../> Gráficos </TabsTrigger>
|
|
||||||
<TabsTrigger value="backups" ...> <Archive .../> Backups </TabsTrigger>
|
|
||||||
<!-- ADICIONAR AQUI: <TabsTrigger value="cientifico" ...> <Brain .../> Científico </TabsTrigger> -->
|
|
||||||
</TabsList>
|
|
||||||
|
|
||||||
<TabsContent value="overview">...</TabsContent>
|
|
||||||
<TabsContent value="datasources">...</TabsContent>
|
|
||||||
<!-- ... outros TabsContent ... -->
|
|
||||||
<!-- ADICIONAR: <TabsContent value="cientifico"><MiroFlowControl /></TabsContent> -->
|
|
||||||
</Tabs>
|
|
||||||
|
|
||||||
<!-- Imports disponíveis em BiWorkspace.tsx — já importados, usar estes -->
|
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
||||||
// Lucide icons já importados: Bot, Brain NÃO está importado — adicionar ao import
|
|
||||||
// shadcn/ui components já disponíveis: Button, Card, CardContent, CardHeader, CardTitle,
|
|
||||||
// Badge, ScrollArea, Input, Textarea, Select, SelectContent, SelectItem, SelectTrigger, SelectValue
|
|
||||||
|
|
||||||
<!-- API pattern usado no projeto (apiRequest de wouter/queryClient) -->
|
|
||||||
// Verificar como outros componentes fazem POST — geralmente:
|
|
||||||
import { apiRequest } from "@/lib/queryClient";
|
|
||||||
// ou via useMutation do @tanstack/react-query
|
|
||||||
const mutation = useMutation({ mutationFn: (body) => apiRequest("POST", "/api/miroflow/analyze", body) });
|
|
||||||
|
|
||||||
<!-- AnalyzeResponse do backend (03-02) -->
|
|
||||||
interface AnalyzeResponse {
|
|
||||||
agent: string;
|
|
||||||
model: string;
|
|
||||||
result: string;
|
|
||||||
execution_id: string;
|
|
||||||
duration_ms: number;
|
|
||||||
}
|
|
||||||
</interfaces>
|
|
||||||
</context>
|
|
||||||
|
|
||||||
<tasks>
|
|
||||||
|
|
||||||
<task type="auto">
|
|
||||||
<name>Task 1: Criar client/src/components/MiroFlowControl.tsx</name>
|
|
||||||
<files>client/src/components/MiroFlowControl.tsx</files>
|
|
||||||
|
|
||||||
<read_first>
|
|
||||||
- /opt/arcadia_merged/client/src/lib/queryClient.ts (confirmar apiRequest signature)
|
|
||||||
- /opt/arcadia_merged/client/src/pages/BiWorkspace.tsx linhas 1-60 (imports disponíveis e padrão de componentes)
|
|
||||||
- /opt/arcadia_merged/client/src/components/ (listar arquivos para verificar convenções de nomenclatura)
|
|
||||||
</read_first>
|
|
||||||
|
|
||||||
<action>
|
|
||||||
Criar client/src/components/MiroFlowControl.tsx como componente React standalone.
|
|
||||||
|
|
||||||
O componente deve:
|
|
||||||
1. Exibir card com header "Modo Científico" e ícone Brain
|
|
||||||
2. Toggle switch ou badge "Ativado" quando em modo científico (estado local `enabled`)
|
|
||||||
3. Select para escolha do agente: Statistician (deepseek-r1:14b), Fiscal Auditor (deepseek-r1:14b), Researcher (llama3.1:8b)
|
|
||||||
4. Textarea para digitar a tarefa (placeholder específico por agente)
|
|
||||||
5. Botão "Analisar" que dispara POST /api/miroflow/analyze
|
|
||||||
6. Área de resultado com ScrollArea mostrando a resposta formatada
|
|
||||||
7. Estado de loading com Loader2 animado durante chamada
|
|
||||||
8. Exibir erros de forma legível (badge vermelho + mensagem)
|
|
||||||
9. Metadados do resultado: modelo usado, tempo de execução, execution_id (em texto pequeno)
|
|
||||||
|
|
||||||
Props: `currentDashboardData?: Record<string, unknown>` (opcional, para contexto futuro)
|
|
||||||
|
|
||||||
Usar useMutation do @tanstack/react-query para a chamada POST.
|
|
||||||
|
|
||||||
Estrutura de tipos locais:
|
|
||||||
```typescript
|
|
||||||
interface AnalyzeRequest {
|
|
||||||
agent: "statistician" | "fiscal_auditor" | "researcher";
|
|
||||||
task: string;
|
|
||||||
context?: Record<string, unknown>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AnalyzeResponse {
|
|
||||||
agent: string;
|
|
||||||
model: string;
|
|
||||||
result: string;
|
|
||||||
execution_id: string;
|
|
||||||
duration_ms: number;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Agentes disponíveis (hardcoded, sem fetch):
|
|
||||||
```typescript
|
|
||||||
const AGENTS = [
|
|
||||||
{ value: "statistician", label: "Statistician", model: "deepseek-r1:14b",
|
|
||||||
placeholder: "Ex: Analise a distribuição de vendas por região no último trimestre" },
|
|
||||||
{ value: "fiscal_auditor", label: "Fiscal Auditor", model: "deepseek-r1:14b",
|
|
||||||
placeholder: "Ex: Verifique inconsistências nos registros NFe do CNPJ 12.345.678/0001-90" },
|
|
||||||
{ value: "researcher", label: "Researcher", model: "llama3.1:8b",
|
|
||||||
placeholder: "Ex: Quais fornecedores têm maior correlação com atrasos de entrega?" },
|
|
||||||
] as const;
|
|
||||||
```
|
|
||||||
|
|
||||||
Estilização: seguir o padrão dark de BiWorkspace (bg-[#1f334d], text-white, border-[#c89b3c]/20, text-[#c89b3c]).
|
|
||||||
|
|
||||||
NÃO usar estados globais, stores ou contextos externos.
|
|
||||||
NÃO fazer fetch para /api/miroflow/health neste componente.
|
|
||||||
</action>
|
|
||||||
|
|
||||||
<verify>
|
|
||||||
<automated>cd /opt/arcadia_merged && npx tsc --noEmit --project tsconfig.json 2>&1 | grep -i "MiroFlowControl\|miroflow" | head -10 || echo "TypeScript OK"</automated>
|
|
||||||
</verify>
|
|
||||||
|
|
||||||
<acceptance_criteria>
|
|
||||||
- client/src/components/MiroFlowControl.tsx existe e exporta MiroFlowControl como default ou named export
|
|
||||||
- `npx tsc --noEmit` não retorna erros relacionados a MiroFlowControl.tsx
|
|
||||||
- Componente tem prop `currentDashboardData?: Record<string, unknown>`
|
|
||||||
- Select com os 3 agentes (statistician, fiscal_auditor, researcher)
|
|
||||||
- Textarea para task com placeholder dinâmico por agente selecionado
|
|
||||||
- useMutation configurado para POST /api/miroflow/analyze
|
|
||||||
- Estado de loading exibido (Loader2 ou spinner equivalente)
|
|
||||||
- Erro exibido quando mutation.isError
|
|
||||||
- Resultado exibido quando mutation.isSuccess com metadados (model, duration_ms)
|
|
||||||
- Nenhuma referência a modelos acima de 14B
|
|
||||||
</acceptance_criteria>
|
|
||||||
|
|
||||||
<done>MiroFlowControl.tsx criado, compila sem erros TypeScript. Componente pronto para ser importado pelo BiWorkspace.</done>
|
|
||||||
</task>
|
|
||||||
|
|
||||||
<task type="auto">
|
|
||||||
<name>Task 2: Adicionar tab "Científico" ao BiWorkspace.tsx</name>
|
|
||||||
<files>client/src/pages/BiWorkspace.tsx</files>
|
|
||||||
|
|
||||||
<read_first>
|
|
||||||
- /opt/arcadia_merged/client/src/pages/BiWorkspace.tsx linhas 2860-2920 (TabsList atual — localizar exatamente onde inserir)
|
|
||||||
- /opt/arcadia_merged/client/src/pages/BiWorkspace.tsx linhas 1-55 (imports para adicionar Brain e MiroFlowControl)
|
|
||||||
- /opt/arcadia_merged/client/src/pages/BiWorkspace.tsx linhas 2950-3050 (após os TabsContent existentes — localizar o fechamento para inserir novo TabsContent)
|
|
||||||
</read_first>
|
|
||||||
|
|
||||||
<action>
|
|
||||||
BiWorkspace.tsx é um arquivo grande (~3000 linhas). Fazer APENAS as 3 modificações cirúrgicas abaixo:
|
|
||||||
|
|
||||||
MODIFICAÇÃO 1 — Adicionar Brain ao import de lucide-react (linha ~10-50):
|
|
||||||
Localizar `import { ... } from "lucide-react"` e adicionar `Brain` à lista.
|
|
||||||
(Verificar se Brain já está importado antes de adicionar)
|
|
||||||
|
|
||||||
MODIFICAÇÃO 2 — Adicionar MiroFlowControl import (após os imports existentes):
|
|
||||||
```typescript
|
|
||||||
import { MiroFlowControl } from "@/components/MiroFlowControl";
|
|
||||||
```
|
|
||||||
|
|
||||||
MODIFICAÇÃO 3 — Adicionar TabsTrigger na TabsList (após o último TabsTrigger existente, antes do fechamento </TabsList>):
|
|
||||||
```tsx
|
|
||||||
<TabsTrigger value="cientifico" className="data-[state=active]:bg-[#c89b3c] data-[state=active]:text-[#1f334d] text-white/70">
|
|
||||||
<Brain className="w-4 h-4 mr-2" /> Científico
|
|
||||||
</TabsTrigger>
|
|
||||||
```
|
|
||||||
|
|
||||||
MODIFICAÇÃO 4 — Adicionar TabsContent (após o último TabsContent existente, antes do fechamento </Tabs>):
|
|
||||||
```tsx
|
|
||||||
<TabsContent value="cientifico">
|
|
||||||
<MiroFlowControl />
|
|
||||||
</TabsContent>
|
|
||||||
```
|
|
||||||
|
|
||||||
NÃO alterar nenhum TabsTrigger ou TabsContent existente.
|
|
||||||
NÃO modificar o estado activeTab ou qualquer lógica existente.
|
|
||||||
NÃO reformatar ou reorganizar o código existente.
|
|
||||||
NÃO remover nenhum import existente.
|
|
||||||
Verificar o arquivo antes e depois com grep para confirmar que os 4 tabs originais ainda existem.
|
|
||||||
</action>
|
|
||||||
|
|
||||||
<verify>
|
|
||||||
<automated>cd /opt/arcadia_merged && grep -c 'TabsTrigger value=' client/src/pages/BiWorkspace.tsx && npx tsc --noEmit --project tsconfig.json 2>&1 | grep -i "BiWorkspace\|MiroFlow" | head -5 || echo "TypeScript OK"</automated>
|
|
||||||
</verify>
|
|
||||||
|
|
||||||
<acceptance_criteria>
|
|
||||||
- `grep -c 'TabsTrigger value=' client/src/pages/BiWorkspace.tsx` retorna 7 (6 existentes + 1 novo "cientifico")
|
|
||||||
- `grep 'value="cientifico"' client/src/pages/BiWorkspace.tsx` retorna 2 linhas (TabsTrigger + TabsContent)
|
|
||||||
- `grep 'Brain' client/src/pages/BiWorkspace.tsx` retorna resultado (importado e usado)
|
|
||||||
- `grep 'MiroFlowControl' client/src/pages/BiWorkspace.tsx` retorna resultado (importado e usado)
|
|
||||||
- `npx tsc --noEmit` não retorna erros em BiWorkspace.tsx
|
|
||||||
- Tabs existentes ("overview", "datasources", "upload", "datasets", "charts", "backups") ainda presentes
|
|
||||||
- Nenhum outro código do arquivo foi alterado (git diff mostra apenas as 4 adições)
|
|
||||||
</acceptance_criteria>
|
|
||||||
|
|
||||||
<done>BiWorkspace.tsx contém tab "Científico" que renderiza MiroFlowControl. Tabs existentes preservadas. Compila sem erros.</done>
|
|
||||||
</task>
|
|
||||||
|
|
||||||
<task type="checkpoint:human-verify" gate="blocking">
|
|
||||||
<what-built>
|
|
||||||
- MiroFlowControl.tsx: componente com toggle "Modo Científico", seletor de 3 agentes, textarea de tarefa e display de resultado
|
|
||||||
- BiWorkspace.tsx: tab "Científico" adicionada (7 tabs no total)
|
|
||||||
- Backend (planos 01 e 02): microserviço Python porta 8006 + proxy Node + KG logging
|
|
||||||
</what-built>
|
|
||||||
|
|
||||||
<how-to-verify>
|
|
||||||
1. Iniciar o servidor: `npm run dev` (ou verificar se já está rodando)
|
|
||||||
2. Iniciar o microserviço MiroFlow: `MIROFLOW_PORT=8006 python3 server/python/miroflow_service.py &`
|
|
||||||
3. Navegar para /bi-workspace no browser
|
|
||||||
4. Verificar que a tab "Científico" aparece no TabsList (ícone Brain + texto)
|
|
||||||
5. Clicar na tab "Científico"
|
|
||||||
6. Verificar que o painel MiroFlowControl renderiza sem erros JavaScript no console
|
|
||||||
7. Selecionar o agente "Statistician"
|
|
||||||
8. Digitar uma tarefa simples: "Liste os 3 principais insights sobre análise estatística"
|
|
||||||
9. Clicar em "Analisar"
|
|
||||||
10. Verificar que o estado de loading aparece (Loader2 girando)
|
|
||||||
11. Após resposta: verificar que o resultado é exibido com o modelo (deepseek-r1:14b) e tempo de execução
|
|
||||||
12. Verificar GET http://localhost:8006/health retorna {"online": true} em outro aba
|
|
||||||
13. Verificar as outras 6 tabs (overview, datasources, etc.) ainda funcionam normalmente
|
|
||||||
</how-to-verify>
|
|
||||||
|
|
||||||
<resume-signal>
|
|
||||||
Digite "aprovado" se tudo funcionar corretamente.
|
|
||||||
Ou descreva os problemas encontrados (ex: "tab não aparece", "erro 502 na análise", "loading infinito").
|
|
||||||
</resume-signal>
|
|
||||||
</task>
|
|
||||||
|
|
||||||
</tasks>
|
|
||||||
|
|
||||||
<verification>
|
|
||||||
```bash
|
|
||||||
# Verificar estrutura dos arquivos:
|
|
||||||
ls /opt/arcadia_merged/client/src/components/MiroFlowControl.tsx
|
|
||||||
|
|
||||||
# Verificar tab adicionada:
|
|
||||||
grep -n 'cientifico\|Científico\|MiroFlowControl\|Brain' /opt/arcadia_merged/client/src/pages/BiWorkspace.tsx
|
|
||||||
|
|
||||||
# Verificar compilação:
|
|
||||||
cd /opt/arcadia_merged && npx tsc --noEmit 2>&1 | tail -5
|
|
||||||
|
|
||||||
# Contagem de tabs:
|
|
||||||
grep -c 'TabsTrigger value=' /opt/arcadia_merged/client/src/pages/BiWorkspace.tsx
|
|
||||||
```
|
|
||||||
</verification>
|
|
||||||
|
|
||||||
<success_criteria>
|
|
||||||
- MiroFlowControl.tsx existe e exporta componente válido
|
|
||||||
- BiWorkspace.tsx tem 7 TabsTriggers (6 originais + "cientifico")
|
|
||||||
- TypeScript compila sem erros nos novos arquivos
|
|
||||||
- Usuário aprova verificação visual do checkpoint
|
|
||||||
- Tabs existentes do BiWorkspace continuam funcionando
|
|
||||||
</success_criteria>
|
|
||||||
|
|
||||||
<output>
|
|
||||||
Após conclusão e aprovação do checkpoint, criar `.planning/phases/03-miroflow-embutido/03-03-SUMMARY.md` com:
|
|
||||||
- Arquivos criados/modificados
|
|
||||||
- Screenshot ou descrição do estado visual da tab "Científico"
|
|
||||||
- Resultado da verificação humana
|
|
||||||
- Qualquer ajuste feito após o checkpoint
|
|
||||||
</output>
|
|
||||||
|
|
@ -1,551 +0,0 @@
|
||||||
# Phase 3: MiroFlow Embutido - Research
|
|
||||||
|
|
||||||
**Researched:** 2026-03-25
|
|
||||||
**Domain:** Python agent framework (MiroFlow) integrado ao Node.js/Express + Ollama local + Superset bridge
|
|
||||||
**Confidence:** HIGH (baseado em inspeção direta do código-fonte e serviços ativos)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
O Phase 3 conecta o framework Python MiroFlow (já clonado como submodule em `server/modules/miroflow/`) ao backend Node.js do Arcádia Suite, expondo um endpoint `POST /api/miroflow/analyze` que despacha para um microserviço Python FastAPI. Esse microserviço instancia agentes MiroFlow configurados via YAML, aponta para o Ollama local (já rodando na porta 11434 com `deepseek-r1:14b` instalado), e registra cada execução como nó imutável no KG (tabela `graph_nodes` via `/api/graph/nodes`).
|
|
||||||
|
|
||||||
O padrão de integração Node ↔ Python já está estabelecido no projeto: `server/bi/engine-proxy.ts` e `server/python/bi_analysis_service.py` demonstram o fluxo exato — Express proxy faz `fetch()` para FastAPI local; a resposta é repassada ao cliente. O novo microserviço MiroFlow (`miroflow_service.py`) seguirá o mesmo padrão, rodando na porta 8006. O agente `Researcher` exige `llama3.1:8b`, que **ainda não está instalado no Ollama** (apenas `deepseek-r1:14b`, `llama3.2:3b` e `nomic-embed-text` estão disponíveis).
|
|
||||||
|
|
||||||
No frontend, o componente `MiroFlowControl.tsx` será um painel overlay/tab injetado na página `BiWorkspace.tsx` que já usa o `SupersetDashboard` component. O toggle "Modo Científico" chama `POST /api/miroflow/analyze` e exibe a resposta estruturada ao lado do dashboard Superset.
|
|
||||||
|
|
||||||
**Primary recommendation:** Construir `server/python/miroflow_service.py` como FastAPI standalone (porta 8006), instanciar agentes MiroFlow com YAML inline (sem arquivos externos), usar `UnifiedOpenAIClient` apontando para Ollama via `OLLAMA_BASE_URL/v1`, registrar execuções via `createNode()` do graph service.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## User Constraints
|
|
||||||
|
|
||||||
*(Seção obrigatória — copiada do STATE.md/ROADMAP.md/PROJECT.md que fazem as vezes de CONTEXT.md)*
|
|
||||||
|
|
||||||
### Locked Decisions
|
|
||||||
- Stack: Node.js + TypeScript backend, React + TypeScript frontend, PostgreSQL, Neo4j KG
|
|
||||||
- IA: Ollama local MÁXIMO 14B — `deepseek-r1:14b` para Statistician e Fiscal Auditor, `llama3.1:8b` para Researcher
|
|
||||||
- **NUNCA usar modelos acima de 14B**: proibido citar ou planejar `deepseek-r1:32b`, `deepseek-r1:70b`, `llama3.1:70b` ou qualquer modelo maior
|
|
||||||
- MiroFlow já clonado em `server/modules/miroflow/` como submodule Python
|
|
||||||
- Branch de deploy: `Servidor`
|
|
||||||
- Superset em produção com RLS — não alterar configurações existentes
|
|
||||||
- Backend-first: API definida antes do frontend
|
|
||||||
- Auditoria imutável em todas as execuções (KG)
|
|
||||||
|
|
||||||
### Claude's Discretion
|
|
||||||
- Porta do microserviço MiroFlow (recomenda-se 8006 pelo padrão existente)
|
|
||||||
- Estrutura interna dos agentes MiroFlow (YAML inline vs arquivos externos)
|
|
||||||
- Forma de integrar o toggle no frontend (overlay, tab, painel lateral)
|
|
||||||
- Como fazer o `llama3.1:8b` ser baixado (wave 0 task vs setup manual)
|
|
||||||
|
|
||||||
### Deferred Ideas (OUT OF SCOPE)
|
|
||||||
- Versionamento Git-like de skills (Phase 2 pendente, não Phase 3)
|
|
||||||
- OpenClaw (Phase 4)
|
|
||||||
- Automation Fabric (Phase 5)
|
|
||||||
- Dev Center Completo (Phase 6)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Standard Stack
|
|
||||||
|
|
||||||
### Core
|
|
||||||
| Library | Version | Purpose | Why Standard |
|
|
||||||
|---------|---------|---------|--------------|
|
|
||||||
| MiroFlow | 1.7.0 | Framework de agentes Python | Já no submodule, desenvolvido pela MiromindAI |
|
|
||||||
| FastAPI | >=0.115.0 | Microserviço Python | Padrão já usado em todos os serviços Python do projeto |
|
|
||||||
| uvicorn | >=0.32.0 | ASGI server | Usado em todos os serviços Python |
|
|
||||||
| omegaconf | (via miroflow deps) | Configuração YAML com variáveis | Dependência central do MiroFlow |
|
|
||||||
| hydra-core | >=1.3.2 | Composição de configurações | Dependência central do MiroFlow |
|
|
||||||
| openai | ==1.78.1 | Cliente HTTP para Ollama (OpenAI-compat.) | MiroFlow usa `UnifiedOpenAIClient` via openai SDK |
|
|
||||||
| httpx / aiohttp | >=0.28.1 / >=3.12.15 | HTTP async | Dependências do MiroFlow e FastAPI |
|
|
||||||
|
|
||||||
### Supporting
|
|
||||||
| Library | Version | Purpose | When to Use |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| python-dotenv | >=1.1.1 | Leitura de .env | Inicialização do microserviço |
|
|
||||||
| pydantic | >=2.x | Validação de request/response | Models FastAPI |
|
|
||||||
| neo4j (driver) | opcional | Neo4j direto | Apenas se precisar grafo real; por ora usa `graph_nodes` PostgreSQL |
|
|
||||||
| psycopg2-binary | >=2.9.x | PostgreSQL direto | Se microserviço precisar escrever no banco |
|
|
||||||
|
|
||||||
### Alternatives Considered
|
|
||||||
| Instead of | Could Use | Tradeoff |
|
|
||||||
|------------|-----------|----------|
|
|
||||||
| Microserviço FastAPI separado | Subprocess Node → Python | subprocess é síncrono e frágil; FastAPI segue padrão estabelecido |
|
|
||||||
| YAML files externos | YAML inline (dict) em Python | Inline elimina dependência de caminhos de arquivo em container |
|
|
||||||
| Ollama direto (porta 11434) | Via LiteLLM gateway (porta 4000) | LiteLLM tem fallback e roteamento; Ollama direto é mais simples para agentes isolados |
|
|
||||||
|
|
||||||
**Installation (para o microserviço):**
|
|
||||||
```bash
|
|
||||||
# Instalar dependências no ambiente Python do servidor
|
|
||||||
pip install fastapi uvicorn omegaconf hydra-core openai python-dotenv pydantic psycopg2-binary
|
|
||||||
# Ou instalar o miroflow inteiro com uv:
|
|
||||||
cd server/modules/miroflow && uv sync
|
|
||||||
```
|
|
||||||
|
|
||||||
**Version verification:** Os pacotes acima são os listados no `pyproject.toml` do MiroFlow v1.7.0, verificado diretamente no arquivo.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Architecture Patterns
|
|
||||||
|
|
||||||
### Recommended Project Structure
|
|
||||||
```
|
|
||||||
server/python/
|
|
||||||
├── miroflow_service.py # novo microserviço FastAPI (porta 8006)
|
|
||||||
├── bi_analysis_service.py # existente (referência de padrão)
|
|
||||||
└── ...
|
|
||||||
|
|
||||||
server/miroflow/
|
|
||||||
├── routes.ts # Express router: POST /api/miroflow/analyze
|
|
||||||
└── engine-proxy.ts # fetch() para http://localhost:8006
|
|
||||||
|
|
||||||
client/src/
|
|
||||||
├── pages/BiWorkspace.tsx # EXISTENTE — adicionar tab "Científico"
|
|
||||||
└── components/
|
|
||||||
└── MiroFlowControl.tsx # NOVO — toggle + resultado da análise
|
|
||||||
|
|
||||||
docker/python-entrypoint.sh # adicionar case "miroflow" → porta 8006
|
|
||||||
docker-compose.prod.yml # adicionar serviço miroflow (porta 8006)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Pattern 1: Node → Python Microservice Proxy
|
|
||||||
**What:** Express recebe a request, faz fetch() para FastAPI, retorna o resultado. Sem subprocess, sem child_process.
|
|
||||||
**When to use:** Sempre que Node.js precisar de lógica Python (pandas, ML, agentes).
|
|
||||||
**Example:**
|
|
||||||
```typescript
|
|
||||||
// Source: server/bi/engine-proxy.ts (padrão existente)
|
|
||||||
const MIROFLOW_URL = `http://${process.env.MIROFLOW_HOST || "localhost"}:${process.env.MIROFLOW_PORT || "8006"}`;
|
|
||||||
|
|
||||||
async function proxyToMiroFlow(path: string, body: object): Promise<any> {
|
|
||||||
const response = await fetch(`${MIROFLOW_URL}${path}`, {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify(body),
|
|
||||||
signal: AbortSignal.timeout(300_000), // 5min — modelos LLM são lentos
|
|
||||||
});
|
|
||||||
if (!response.ok) {
|
|
||||||
const err = await response.json().catch(() => ({ detail: response.statusText }));
|
|
||||||
throw new Error(err.detail || `MiroFlow error: ${response.status}`);
|
|
||||||
}
|
|
||||||
return response.json();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Pattern 2: MiroFlow Agent com Ollama via UnifiedOpenAIClient
|
|
||||||
**What:** MiroFlow usa `UnifiedOpenAIClient` com `base_url` apontando para Ollama (OpenAI-compatible API em `/v1`).
|
|
||||||
**When to use:** Para todos os agentes do Phase 3.
|
|
||||||
**Example:**
|
|
||||||
```python
|
|
||||||
# Source: server/modules/miroflow/miroflow/llm/openai_client.py + config/llm/base.yaml
|
|
||||||
from miroflow.agents.factory import build_agent
|
|
||||||
from miroflow.agents.context import AgentContext
|
|
||||||
|
|
||||||
AGENT_CONFIG_STATISTICIAN = {
|
|
||||||
"type": "IterativeAgentWithToolAndRollback",
|
|
||||||
"max_turns": 10,
|
|
||||||
"llm": {
|
|
||||||
"provider_class": "UnifiedOpenAIClient",
|
|
||||||
"model_name": "deepseek-r1:14b",
|
|
||||||
"api_key": "ollama", # Ollama não valida api_key
|
|
||||||
"base_url": "http://localhost:11434/v1", # Ollama OpenAI-compat
|
|
||||||
"max_tokens": 8192,
|
|
||||||
"temperature": 0.7,
|
|
||||||
"top_p": 1.0, "min_p": 0.0, "top_k": -1,
|
|
||||||
"reasoning_effort": None, "repetition_penalty": 1.0,
|
|
||||||
"max_context_length": -1, "async_client": True,
|
|
||||||
"disable_cache_control": True, "keep_tool_result": -1,
|
|
||||||
"use_tool_calls": False, "oai_tool_thinking": False,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
agent = build_agent(AGENT_CONFIG_STATISTICIAN)
|
|
||||||
ctx = AgentContext(task_description="Analise os seguintes dados SQL: ...")
|
|
||||||
result = await agent.run(ctx)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Pattern 3: Registro Imutável no KG via graph_nodes
|
|
||||||
**What:** Após cada execução MiroFlow, registrar no PostgreSQL via `POST /api/graph/nodes`.
|
|
||||||
**When to use:** Toda execução de agente (success ou error).
|
|
||||||
**Example:**
|
|
||||||
```typescript
|
|
||||||
// Source: server/graph/service.ts createNode() — chamado pelo routes.ts de miroflow
|
|
||||||
await fetch("/api/graph/nodes", {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify({
|
|
||||||
type: "miroflow_execution",
|
|
||||||
tenantId: req.user?.tenantId,
|
|
||||||
data: {
|
|
||||||
agent: "statistician",
|
|
||||||
model: "deepseek-r1:14b",
|
|
||||||
input: analysisRequest,
|
|
||||||
output: analysisResult,
|
|
||||||
auditHash: sha256(JSON.stringify({ executionId, input, output })),
|
|
||||||
executedAt: new Date().toISOString(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Pattern 4: MiroFlowControl.tsx como tab em BiWorkspace
|
|
||||||
**What:** BiWorkspace.tsx já tem estrutura de tabs (Tabs/TabsContent de shadcn/ui). Adicionar tab "Científico" que renderiza `MiroFlowControl`.
|
|
||||||
**When to use:** Ponto de entrada do toggle "Modo Científico" no contexto do BI.
|
|
||||||
**Example:**
|
|
||||||
```tsx
|
|
||||||
// Source: client/src/pages/BiWorkspace.tsx (padrão de tabs existente)
|
|
||||||
import { MiroFlowControl } from "@/components/MiroFlowControl";
|
|
||||||
|
|
||||||
// Dentro do <TabsList>:
|
|
||||||
<TabsTrigger value="cientifico"><Brain /> Científico</TabsTrigger>
|
|
||||||
|
|
||||||
// Dentro das <TabsContent>:
|
|
||||||
<TabsContent value="cientifico">
|
|
||||||
<MiroFlowControl currentDashboardData={currentData} />
|
|
||||||
</TabsContent>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Anti-Patterns to Avoid
|
|
||||||
- **subprocess.run() no Node**: Não usar `child_process.spawn()` para chamar Python — use o microserviço FastAPI (padrão estabelecido)
|
|
||||||
- **Carregar miroflow no container Node**: O Dockerfile principal é Node.js puro — Python deve rodar em container separado (Dockerfile.python)
|
|
||||||
- **Modelos > 14B**: Nunca usar `deepseek-r1:32b`, `deepseek-r1:70b`, `llama3.1:70b` ou qualquer modelo acima de 14B parâmetros
|
|
||||||
- **Modificar configurações do Superset**: O proxy `/superset/*` e as rotas `/api/superset/*` existentes não devem ser alterados
|
|
||||||
- **Hardcoded OLLAMA_BASE_URL**: Sempre ler de env var `OLLAMA_BASE_URL` (default `http://localhost:11434`)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Don't Hand-Roll
|
|
||||||
|
|
||||||
| Problem | Don't Build | Use Instead | Why |
|
|
||||||
|---------|-------------|-------------|-----|
|
|
||||||
| Orquestração de agentes LLM | Loop manual de chamadas LLM | MiroFlow `IterativeAgentWithToolAndRollback` | Rollback automático, retry, tool calling já implementado |
|
|
||||||
| Proxy Node → Python | Child process / spawn | `fetch()` para FastAPI (padrão `engine-proxy.ts`) | Stateless, async, sem gestão de processos |
|
|
||||||
| Configuração de LLM | Classe própria de cliente | `UnifiedOpenAIClient` do MiroFlow | Já tem retry, context limit handling, tool protocol |
|
|
||||||
| Embedding da análise no Superset | iframe manual com JS | `SupersetDashboard.tsx` já existente | Guest token, SDK do Superset, error handling prontos |
|
|
||||||
| Logs de execução imutáveis | Nova tabela no banco | `graph_nodes` + `createNode()` via `server/graph/service.ts` | Infraestrutura de KG já existente com embeddings |
|
|
||||||
| Validação de request/response | Manual | Pydantic models + FastAPI auto-validation | Type safety e OpenAPI spec grátis |
|
|
||||||
|
|
||||||
**Key insight:** O projeto já tem todos os primitivos necessários. Phase 3 é essencialmente "conectar os pontos" — o agente Python já existe, o gateway LLM (LiteLLM/Ollama) já está configurado, o KG já tem tabelas, o padrão de proxy Node→Python já está implementado.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Runtime State Inventory
|
|
||||||
|
|
||||||
> Fase é greenfield (novo microserviço e novas rotas). Não há renomeações ou migrações de dados existentes.
|
|
||||||
|
|
||||||
| Category | Items Found | Action Required |
|
|
||||||
|----------|-------------|------------------|
|
|
||||||
| Stored data | `graph_nodes` no PostgreSQL — usado pelo KG existente | Nenhum — novas inserções com `type: "miroflow_execution"` |
|
|
||||||
| Live service config | Ollama rodando na porta 11434 com `deepseek-r1:14b` instalado | `llama3.1:8b` NÃO instalado — precisa de `ollama pull llama3.1:8b` |
|
|
||||||
| OS-registered state | Nenhum Task Scheduler / cron para MiroFlow | Nenhuma ação necessária |
|
|
||||||
| Secrets/env vars | `OLLAMA_BASE_URL=http://ollama:11434` já no `.env`; `LITELLM_API_KEY` existente | Adicionar `MIROFLOW_PORT=8006` no `.env` e docker-compose |
|
|
||||||
| Build artifacts | `server/modules/miroflow/` tem `uv.lock` mas sem `.venv` instalado | Wave 0: instalar dependências via `uv sync` ou `pip install` no Dockerfile.python |
|
|
||||||
|
|
||||||
**Modelo faltando:** `llama3.1:8b` não está instalado no Ollama (`False` confirmado por `curl http://localhost:11434/api/tags`). Instalado: `deepseek-r1:14b` (8.4GB), `arcadia-agent:latest` (2GB), `llama3.2:3b` (2GB), `nomic-embed-text` (274MB). O plano deve incluir `ollama pull llama3.1:8b` como task de Wave 0 ou pré-requisito.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Environment Availability
|
|
||||||
|
|
||||||
| Dependency | Required By | Available | Version | Fallback |
|
|
||||||
|------------|------------|-----------|---------|----------|
|
|
||||||
| Ollama API `:11434` | Todos os agentes MiroFlow | ✓ | 0.18.2 | — |
|
|
||||||
| `deepseek-r1:14b` | Statistician, Fiscal Auditor | ✓ | 8.4GB | — |
|
|
||||||
| `llama3.1:8b` | Researcher | ✗ | — | `llama3.2:3b` (degraded) ou `deepseek-r1:14b` |
|
|
||||||
| Python 3.12 | Microserviço FastAPI | ✓ | 3.12.3 | — |
|
|
||||||
| `uv` | Instalar deps MiroFlow | ✓ | 0.9.30 | `pip3` |
|
|
||||||
| FastAPI/uvicorn | Microserviço | ✓ | fastapi 0.128.8 | — |
|
|
||||||
| `omegaconf` (sistema) | MiroFlow direto | ✗ | — | Instalar via `uv sync` no `server/modules/miroflow/` |
|
|
||||||
| PostgreSQL `:5432` | KG graph_nodes | ✓ | 16 | — |
|
|
||||||
| Neo4j `:7687` | KG real (opcional) | Requer profile `[kg]` | — | `graph_nodes` PostgreSQL (já usado) |
|
|
||||||
|
|
||||||
**Missing dependencies with no fallback:**
|
|
||||||
- `llama3.1:8b` para o agente Researcher — deve ser baixado em Wave 0 (`ollama pull llama3.1:8b`)
|
|
||||||
|
|
||||||
**Missing dependencies with fallback:**
|
|
||||||
- `omegaconf` no sistema: instalar com `pip3 install omegaconf hydra-core` ou rodar MiroFlow dentro de venv com `uv sync`
|
|
||||||
- `llama3.1:8b` temporariamente: usar `deepseek-r1:14b` como fallback até o download completar
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Common Pitfalls
|
|
||||||
|
|
||||||
### Pitfall 1: Ollama não expõe `/v1` sem flag explícita em versões antigas
|
|
||||||
**What goes wrong:** `UnifiedOpenAIClient` do MiroFlow usa `base_url=http://localhost:11434/v1` (OpenAI-compatible endpoint). Versões antigas do Ollama (<0.1.24) não tinham esse endpoint.
|
|
||||||
**Why it happens:** Ollama 0.18.2 (confirmado no servidor) já suporta `/v1/chat/completions`. Mas o endpoint retorna erro se o modelo não estiver carregado.
|
|
||||||
**How to avoid:** Verificar `GET http://localhost:11434/api/tags` antes de iniciar o agente. Pré-carregar o modelo com `ollama run deepseek-r1:14b` ou `ollama pull`.
|
|
||||||
**Warning signs:** `404 Not Found` na URL `/v1/chat/completions`, ou `model not found` no corpo da resposta.
|
|
||||||
|
|
||||||
### Pitfall 2: MiroFlow requer dependências Python não instaladas no container Node
|
|
||||||
**What goes wrong:** O `Dockerfile` principal é Node.js Alpine — não tem Python. Se tentar importar `miroflow` no contexto Node (via python-bridge ou similar), vai falhar.
|
|
||||||
**Why it happens:** A tentativa de `from omegaconf import ...` falha com `ModuleNotFoundError` porque o sistema Python não tem as deps do MiroFlow.
|
|
||||||
**How to avoid:** Manter a separação clara: MiroFlow roda SOMENTE em `Dockerfile.python` como microserviço FastAPI. Node nunca importa Python diretamente.
|
|
||||||
**Warning signs:** Erro de `ModuleNotFoundError: No module named 'omegaconf'` nos logs do app principal.
|
|
||||||
|
|
||||||
### Pitfall 3: Timeout do fetch() insuficiente para modelos grandes
|
|
||||||
**What goes wrong:** `deepseek-r1:14b` com raciocínio pode levar 60-120 segundos para responder. O fetch padrão tem timeout de 30s nos outros proxies.
|
|
||||||
**Why it happens:** O `bi/engine-proxy.ts` usa `BI_ENGINE_TIMEOUT = 30000`. Para LLMs de 14B, isso é insuficiente.
|
|
||||||
**How to avoid:** Usar `AbortSignal.timeout(300_000)` (5 minutos) no proxy MiroFlow. Implementar streaming ou polling se necessário.
|
|
||||||
**Warning signs:** `AbortError: The operation was aborted` nos logs do Node.
|
|
||||||
|
|
||||||
### Pitfall 4: Neo4j não está ativo por padrão — usar graph_nodes do PostgreSQL
|
|
||||||
**What goes wrong:** O `docker-compose.yml` tem Neo4j no profile `[kg]` — não está ativo em deploy padrão.
|
|
||||||
**Why it happens:** Neo4j é opcional; o KG do Arcádia usa `graph_nodes` PostgreSQL por padrão.
|
|
||||||
**How to avoid:** Para imutabilidade, usar `POST /api/graph/nodes` (PostgreSQL), não o driver `neo4j` Python direto. O `server/graph/service.ts` já implementa isso com hashing.
|
|
||||||
**Warning signs:** `Connection refused: 7687` se tentar conectar ao Neo4j sem o profile ativo.
|
|
||||||
|
|
||||||
### Pitfall 5: MiroFlow não tem `llama3.1:8b` instalado
|
|
||||||
**What goes wrong:** O agente Researcher usa `llama3.1:8b` mas esse modelo não está no Ollama (`False` confirmado por inspeção direta).
|
|
||||||
**Why it happens:** Apenas `deepseek-r1:14b`, `arcadia-agent:latest`, `llama3.2:3b` e `nomic-embed-text` foram instalados.
|
|
||||||
**How to avoid:** Incluir `ollama pull llama3.1:8b` como step explícito na Wave 0 do plano.
|
|
||||||
**Warning signs:** Agente Researcher falha com `model 'llama3.1:8b' not found`.
|
|
||||||
|
|
||||||
### Pitfall 6: BiWorkspace.tsx é uma página grande — cuidado ao modificar
|
|
||||||
**What goes wrong:** `BiWorkspace.tsx` tem muitas funcionalidades (datasets, charts, dashboards, backup, BI engine). Modificação descuidada pode quebrar features existentes.
|
|
||||||
**Why it happens:** O arquivo tem ~1000+ linhas com múltiplas tabs e estados.
|
|
||||||
**How to avoid:** Adicionar MiroFlowControl como componente SEPARADO importado, não modificar lógica existente. Adicionar apenas uma nova `TabsTrigger` e `TabsContent`.
|
|
||||||
**Warning signs:** Erros de TypeScript em outros tabs depois da edição.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Code Examples
|
|
||||||
|
|
||||||
Verified patterns from official sources:
|
|
||||||
|
|
||||||
### MiroFlow Agent com Ollama (config inline Python)
|
|
||||||
```python
|
|
||||||
# Source: server/modules/miroflow/miroflow/agents/base.py + config/llm/base.yaml
|
|
||||||
import sys
|
|
||||||
sys.path.insert(0, "/app/server/modules/miroflow")
|
|
||||||
|
|
||||||
import asyncio
|
|
||||||
from miroflow.agents.factory import build_agent
|
|
||||||
from miroflow.agents.context import AgentContext
|
|
||||||
|
|
||||||
OLLAMA_BASE_URL = os.getenv("OLLAMA_BASE_URL", "http://localhost:11434")
|
|
||||||
|
|
||||||
def make_agent_cfg(model: str) -> dict:
|
|
||||||
return {
|
|
||||||
"type": "IterativeAgentWithToolAndRollback",
|
|
||||||
"name": f"arcadia_{model.replace(':', '_')}",
|
|
||||||
"max_turns": 10,
|
|
||||||
"llm": {
|
|
||||||
"provider_class": "UnifiedOpenAIClient",
|
|
||||||
"model_name": model,
|
|
||||||
"api_key": "ollama",
|
|
||||||
"base_url": f"{OLLAMA_BASE_URL}/v1",
|
|
||||||
"max_tokens": 8192,
|
|
||||||
"temperature": 0.7,
|
|
||||||
"top_p": 1.0, "min_p": 0.0, "top_k": -1,
|
|
||||||
"reasoning_effort": None, "repetition_penalty": 1.0,
|
|
||||||
"max_context_length": -1, "async_client": True,
|
|
||||||
"disable_cache_control": True, "keep_tool_result": -1,
|
|
||||||
"use_tool_calls": False, "oai_tool_thinking": False,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
async def run_agent(agent_type: str, task: str) -> str:
|
|
||||||
model = "deepseek-r1:14b" if agent_type in ("statistician", "fiscal_auditor") else "llama3.1:8b"
|
|
||||||
cfg = make_agent_cfg(model)
|
|
||||||
agent = build_agent(cfg)
|
|
||||||
ctx = AgentContext(task_description=task)
|
|
||||||
result = await agent.run(ctx)
|
|
||||||
return result.get("summary", str(result))
|
|
||||||
```
|
|
||||||
|
|
||||||
### FastAPI endpoint pattern (seguindo bi_analysis_service.py)
|
|
||||||
```python
|
|
||||||
# Source: server/python/bi_analysis_service.py (padrão existente)
|
|
||||||
from fastapi import FastAPI
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
app = FastAPI(title="Arcádia MiroFlow Service", version="1.0.0")
|
|
||||||
|
|
||||||
class AnalyzeRequest(BaseModel):
|
|
||||||
agent: str # "statistician" | "fiscal_auditor" | "researcher"
|
|
||||||
task: str
|
|
||||||
context: dict = {}
|
|
||||||
tenant_id: int | None = None
|
|
||||||
|
|
||||||
class AnalyzeResponse(BaseModel):
|
|
||||||
agent: str
|
|
||||||
model: str
|
|
||||||
result: str
|
|
||||||
execution_id: str
|
|
||||||
duration_ms: int
|
|
||||||
|
|
||||||
@app.post("/analyze", response_model=AnalyzeResponse)
|
|
||||||
async def analyze(req: AnalyzeRequest):
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
### Express proxy para MiroFlow (seguindo engine-proxy.ts)
|
|
||||||
```typescript
|
|
||||||
// Source: server/bi/engine-proxy.ts (padrão existente)
|
|
||||||
const MIROFLOW_URL = `http://${process.env.MIROFLOW_HOST || "localhost"}:${
|
|
||||||
process.env.MIROFLOW_PORT || "8006"
|
|
||||||
}`;
|
|
||||||
|
|
||||||
export function registerMiroFlowRoutes(app: Express): void {
|
|
||||||
app.post("/api/miroflow/analyze", async (req: Request, res: Response) => {
|
|
||||||
if (!req.isAuthenticated()) return res.status(401).json({ error: "Não autenticado" });
|
|
||||||
try {
|
|
||||||
const response = await fetch(`${MIROFLOW_URL}/analyze`, {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({ ...req.body, tenant_id: (req.user as any)?.tenantId }),
|
|
||||||
signal: AbortSignal.timeout(300_000),
|
|
||||||
});
|
|
||||||
const data = await response.json();
|
|
||||||
// Registrar no KG
|
|
||||||
await registerExecutionInKG(req, data);
|
|
||||||
res.json(data);
|
|
||||||
} catch (err: any) {
|
|
||||||
res.status(502).json({ error: err.message });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get("/api/miroflow/health", async (_req, res) => {
|
|
||||||
try {
|
|
||||||
const r = await fetch(`${MIROFLOW_URL}/health`, { signal: AbortSignal.timeout(5000) });
|
|
||||||
res.json({ online: r.ok, url: MIROFLOW_URL });
|
|
||||||
} catch {
|
|
||||||
res.json({ online: false, url: MIROFLOW_URL });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Registro no KG (graph_nodes)
|
|
||||||
```typescript
|
|
||||||
// Source: server/graph/service.ts createNode()
|
|
||||||
import { createNode } from "../graph/service";
|
|
||||||
import crypto from "crypto";
|
|
||||||
|
|
||||||
async function registerExecutionInKG(req: any, execData: any) {
|
|
||||||
const auditHash = crypto
|
|
||||||
.createHash("sha256")
|
|
||||||
.update(JSON.stringify(execData))
|
|
||||||
.digest("hex");
|
|
||||||
await createNode({
|
|
||||||
type: "miroflow_execution",
|
|
||||||
tenantId: req.user?.tenantId,
|
|
||||||
data: { ...execData, auditHash, immutable: true },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## State of the Art
|
|
||||||
|
|
||||||
| Old Approach | Current Approach | When Changed | Impact |
|
|
||||||
|--------------|------------------|--------------|--------|
|
|
||||||
| Llamaindex/LangChain para agentes | MiroFlow (framework próprio MiromindAI) | 2025-08 | Performance-first, benchmarks públicos, já no submodule |
|
|
||||||
| OpenAI API direta | Ollama local via LiteLLM gateway | 2025 (Arcádia) | Soberania total dos dados |
|
|
||||||
| Neo4j obrigatório para KG | graph_nodes PostgreSQL + Neo4j opcional | Phase 1 | Sem dependência de serviço extra em produção básica |
|
|
||||||
| Hydra config files externos | Config dict inline (Python) | MiroFlow 1.x | Sem dependência de paths, mais fácil em containers |
|
|
||||||
|
|
||||||
**Deprecated/outdated:**
|
|
||||||
- MiroFlow via CLI (`python run_single_task.py`): adequado para benchmark, mas não para microserviço de produção. Usar `web_app/main.py` como referência OU criar FastAPI própria.
|
|
||||||
- Modelos > 14B: explicitamente proibidos na arquitetura do projeto.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Open Questions
|
|
||||||
|
|
||||||
1. **`llama3.1:8b` vs `llama3.2:3b` para Researcher**
|
|
||||||
- What we know: `llama3.1:8b` não está instalado; `llama3.2:3b` está (2GB, já disponível)
|
|
||||||
- What's unclear: Se João quer baixar `llama3.1:8b` (4.7GB a mais) ou aceitar `llama3.2:3b` como substituto para o Researcher
|
|
||||||
- Recommendation: Plano deve incluir `ollama pull llama3.1:8b` como Wave 0, com fallback para `llama3.2:3b` se bandwidth for problema
|
|
||||||
|
|
||||||
2. **MiroFlow via web_app existente vs FastAPI própria**
|
|
||||||
- What we know: `server/modules/miroflow/web_app/main.py` é uma FastAPI completa com session manager e task executor; `server/python/bi_analysis_service.py` é um FastAPI simples sem estado
|
|
||||||
- What's unclear: Usar o `web_app` completo do MiroFlow (mais funcionalidades) ou escrever `miroflow_service.py` minimal seguindo o padrão de outros serviços
|
|
||||||
- Recommendation: Escrever `miroflow_service.py` minimal — mais simples, sem session management, sem frontend próprio do MiroFlow
|
|
||||||
|
|
||||||
3. **Registro de execuções: graph_nodes vs skill_executions**
|
|
||||||
- What we know: `skill_executions` é específico para Skills Arcádia; `graph_nodes` é KG genérico
|
|
||||||
- What's unclear: O requirement diz "KG" — se é necessário usar o Neo4j real ou PostgreSQL `graph_nodes` é suficiente
|
|
||||||
- Recommendation: Usar `graph_nodes` PostgreSQL com `type: "miroflow_execution"` — atende o requisito de imutabilidade sem depender do Neo4j (profile opcional)
|
|
||||||
|
|
||||||
4. **Posicionamento do MiroFlowControl na UI**
|
|
||||||
- What we know: `BiWorkspace.tsx` já tem sistema de tabs (LayoutDashboard, Database, BarChart3...); `Scientist.tsx` já existe como página standalone de análise científica
|
|
||||||
- What's unclear: Se o toggle deve ficar dentro do BiWorkspace (access contextual) ou se deve ser um botão flutuante ou se Scientist.tsx deve ser integrado ao BI
|
|
||||||
- Recommendation: Tab "Científico" dentro de `BiWorkspace.tsx` — mais coerente com o objetivo "integrado ao Superset"
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Validation Architecture
|
|
||||||
|
|
||||||
> `workflow.nyquist_validation` não encontrado em `.planning/config.json` (arquivo não existe). Tratado como habilitado.
|
|
||||||
|
|
||||||
### Test Framework
|
|
||||||
| Property | Value |
|
|
||||||
|----------|-------|
|
|
||||||
| Framework | pytest (Python) + vitest/jest implícito (TS) |
|
|
||||||
| Config file | Nenhum — Wave 0 deve criar `pytest.ini` para o microserviço |
|
|
||||||
| Quick run command | `pytest server/python/test_miroflow_service.py -x` |
|
|
||||||
| Full suite command | `pytest server/python/ -v` |
|
|
||||||
|
|
||||||
### Phase Requirements → Test Map
|
|
||||||
| Req ID | Behavior | Test Type | Automated Command | File Exists? |
|
|
||||||
|--------|----------|-----------|-------------------|-------------|
|
|
||||||
| REQ-3.1 | MiroFlow configurado para Ollama local ≤14B | integration | `pytest tests/test_miroflow_ollama.py -x` | ❌ Wave 0 |
|
|
||||||
| REQ-3.2 | Agente Statistician analisa SQL com deepseek-r1:14b | integration | `pytest tests/test_miroflow_service.py::test_statistician -x` | ❌ Wave 0 |
|
|
||||||
| REQ-3.3 | Agente Fiscal Auditor valida NFe/SPED com deepseek-r1:14b | integration | `pytest tests/test_miroflow_service.py::test_fiscal_auditor -x` | ❌ Wave 0 |
|
|
||||||
| REQ-3.4 | Agente Researcher consulta KG com llama3.1:8b | integration | `pytest tests/test_miroflow_service.py::test_researcher -x` | ❌ Wave 0 |
|
|
||||||
| REQ-3.5 | `POST /api/miroflow/analyze` retorna análise estruturada | integration | `curl -X POST localhost:5000/api/miroflow/analyze ...` | ❌ Wave 0 |
|
|
||||||
| REQ-3.6 | MiroFlowControl.tsx toggle aparece no Superset/BiWorkspace | manual | Inspeção visual no browser | N/A |
|
|
||||||
| REQ-3.7 | Execuções registradas com imutabilidade no KG | unit | `pytest tests/test_miroflow_kg.py -x` | ❌ Wave 0 |
|
|
||||||
|
|
||||||
### Sampling Rate
|
|
||||||
- **Per task commit:** `pytest server/python/test_miroflow_service.py -x`
|
|
||||||
- **Per wave merge:** `pytest server/python/ -v`
|
|
||||||
- **Phase gate:** Health check `/api/miroflow/health` retorna `{"online": true}` + todos os 3 agentes respondem com modelo correto
|
|
||||||
|
|
||||||
### Wave 0 Gaps
|
|
||||||
- [ ] `server/python/test_miroflow_service.py` — cobre REQ-3.1, 3.2, 3.3, 3.4, 3.5, 3.7
|
|
||||||
- [ ] `server/python/miroflow_service.py` — o microserviço principal
|
|
||||||
- [ ] `server/miroflow/routes.ts` + `engine-proxy.ts` — rotas Express
|
|
||||||
- [ ] `client/src/components/MiroFlowControl.tsx` — componente React
|
|
||||||
- [ ] `ollama pull llama3.1:8b` — modelo necessário para Researcher (Wave 0 setup)
|
|
||||||
- [ ] Instalar dependências MiroFlow: `pip install omegaconf hydra-core openai` (ou `uv sync` no diretório do submodule)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Sources
|
|
||||||
|
|
||||||
### Primary (HIGH confidence)
|
|
||||||
- Inspeção direta: `/opt/arcadia_merged/server/modules/miroflow/pyproject.toml` — versão 1.7.0, dependências confirmadas
|
|
||||||
- Inspeção direta: `/opt/arcadia_merged/server/modules/miroflow/miroflow/llm/openai_client.py` — `UnifiedOpenAIClient`, `base_url` configurável
|
|
||||||
- Inspeção direta: `/opt/arcadia_merged/server/modules/miroflow/miroflow/agents/base.py` — `BaseAgent`, `AgentContext`, `build_agent()`
|
|
||||||
- Inspeção direta: `/opt/arcadia_merged/server/modules/miroflow/config/llm/base.yaml` — campos obrigatórios do LLM config
|
|
||||||
- Inspeção direta: `/opt/arcadia_merged/server/bi/engine-proxy.ts` — padrão estabelecido de proxy Node → Python
|
|
||||||
- Inspeção direta: `/opt/arcadia_merged/server/python/bi_analysis_service.py` — padrão do microserviço FastAPI
|
|
||||||
- Inspeção direta: `/opt/arcadia_merged/docker/litellm-config.yaml` — `deepseek-r1:14b` já configurado como `arcadia-default`
|
|
||||||
- Inspeção direta: `/opt/arcadia_merged/docker-compose.prod.yml` — `OLLAMA_BASE_URL` configurado, serviço `ollama` com profile `[ai]`
|
|
||||||
- Runtime check: `curl http://localhost:11434/api/tags` — modelos instalados confirmados
|
|
||||||
- Runtime check: `curl http://localhost:11434/api/version` — Ollama 0.18.2 com `/v1` support
|
|
||||||
|
|
||||||
### Secondary (MEDIUM confidence)
|
|
||||||
- Inspeção direta: `/opt/arcadia_merged/server/modules/miroflow/miroflow/agents/iterative_agent_with_rollback.py` — tipo `IterativeAgentWithToolAndRollback` confirmado
|
|
||||||
- Inspeção direta: `/opt/arcadia_merged/server/modules/miroflow/web_app/main.py` — FastAPI web_app existente no submodule (alternativa não usada)
|
|
||||||
- Inspeção direta: `/opt/arcadia_merged/server/graph/service.ts` — `createNode()`, `graph_nodes` tabela
|
|
||||||
|
|
||||||
### Tertiary (LOW confidence)
|
|
||||||
- Inferência sobre tempo de resposta do `deepseek-r1:14b` (60-120s) baseada em conhecimento geral de modelos 14B — não medido diretamente no servidor
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Metadata
|
|
||||||
|
|
||||||
**Confidence breakdown:**
|
|
||||||
- Standard stack: HIGH — verificado diretamente nos arquivos do submodule e nas dependências instaladas
|
|
||||||
- Architecture: HIGH — padrões copiados de código existente funcionando em produção
|
|
||||||
- Pitfalls: HIGH (3-5) e MEDIUM (1-2) — baseados em inspeção direta do runtime e código
|
|
||||||
- Ambiente: HIGH — testado com curl direto ao Ollama e listagem de modelos
|
|
||||||
|
|
||||||
**Research date:** 2026-03-25
|
|
||||||
**Valid until:** 2026-04-25 (30 dias; stack estável, Ollama versão fixada)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## RESEARCH COMPLETE
|
|
||||||
|
|
@ -1,97 +0,0 @@
|
||||||
# Phase 4 Context — OpenClaw Embutido
|
|
||||||
|
|
||||||
> Generated in --auto mode on 2026-03-26. All decisions auto-selected with recommended defaults.
|
|
||||||
|
|
||||||
## Phase Goal
|
|
||||||
|
|
||||||
Skills emergentes criadas automaticamente a partir de padrões detectados em `skill_executions`.
|
|
||||||
|
|
||||||
## Prior Context Applied
|
|
||||||
|
|
||||||
- **Stack de IA:** Ollama local via LiteLLM — usar `llama3.1:8b` para geração de drafts (leve, rápido)
|
|
||||||
- **Backend-first:** API definida antes do frontend
|
|
||||||
- **Auditoria imutável:** todas as execuções registradas com SHA-256
|
|
||||||
- **Schema já existe:** `detected_patterns` e `skill_suggestions` em `shared/schema.ts` — usar sem alterar
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Decisions
|
|
||||||
|
|
||||||
### 1. O que constitui um "padrão"?
|
|
||||||
|
|
||||||
**[auto] Mesma skill executada ≥3 vezes em 30 dias com confiança ≥80%**
|
|
||||||
|
|
||||||
- Fonte de dados: tabela `skill_executions`
|
|
||||||
- Agrupamento: `skillId + userId` (por usuário, não global)
|
|
||||||
- Confiança calculada como: `(frequency / max_frequency_in_window) * 100`
|
|
||||||
- Janela: `lastSeenAt - firstSeenAt ≤ 30 dias`
|
|
||||||
- Thresholds alinhados com o roadmap: min 3 ocorrências, 30 dias, 80% confiança
|
|
||||||
|
|
||||||
### 2. Trigger do PatternDetector
|
|
||||||
|
|
||||||
**[auto] Cron job a cada hora (setInterval no servidor Node.js)**
|
|
||||||
|
|
||||||
- Sem dependência de infraestrutura externa (sem Redis, sem Bull)
|
|
||||||
- Ao iniciar, PatternDetector registra no startup do servidor
|
|
||||||
- Execuções recentes analisadas em lote; padrões gravados em `detected_patterns`
|
|
||||||
- Se padrão já existe (mesmo skillId + userId): atualiza frequency + lastSeenAt
|
|
||||||
|
|
||||||
### 3. Geração do body da skill emergente
|
|
||||||
|
|
||||||
**[auto] AI via llama3.1:8b (LiteLLM) gera o body do DRAFT**
|
|
||||||
|
|
||||||
- Ao atingir threshold: PatternDetector cria entrada em `skill_suggestions` (status: `pending`)
|
|
||||||
- Skill DRAFT gerada automaticamente em `arcadia_skills` (status: `draft`, source: `openclaw`)
|
|
||||||
- Body gerado via prompt para llama3.1:8b descrevendo o padrão detectado
|
|
||||||
- DRAFT aguarda aprovação — não é executável até ser `published`
|
|
||||||
|
|
||||||
### 4. Widget de notificação
|
|
||||||
|
|
||||||
**[auto] Badge no header global + painel slide-in**
|
|
||||||
|
|
||||||
- Badge no ícone de notificações existente no header (sem criar novo componente de header)
|
|
||||||
- Ao clicar: slide-in panel lateral mostrando lista de sugestões pendentes
|
|
||||||
- Cada sugestão mostra: nome sugerido, padrão detectado, frequência, confiança, body preview
|
|
||||||
- Ações inline: "Aceitar" → promove para skill publicada | "Rejeitar" → status `rejected`
|
|
||||||
|
|
||||||
### 5. Aprovação — onde?
|
|
||||||
|
|
||||||
**[auto] Tab "Sugestões" em `/skills` (página existente) — Dev Center completo na Phase 6**
|
|
||||||
|
|
||||||
- Sem criar nova rota — adicionar tab na página `/skills` já existente
|
|
||||||
- Tab lista `skill_suggestions` com status `pending`
|
|
||||||
- Fluxo: aceitar → cria skill publicada a partir do DRAFT | rejeitar → arquiva sugestão
|
|
||||||
- Dev Center completo (Phase 6) herdará esse fluxo
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Reusable Assets Identified
|
|
||||||
|
|
||||||
- `shared/schema.ts` — `detectedPatterns`, `skillSuggestions` já definidos (Phase 1)
|
|
||||||
- `server/skills/engine.ts` — SkillEngine para criar DRAFT skills programaticamente
|
|
||||||
- `server/skills/routes.ts` — rota `/api/skills` existente, adicionar sub-rotas de sugestões
|
|
||||||
- `server/skills/versioning.ts` — SHA-256 audit logging (reusar padrão)
|
|
||||||
- `client/src/pages/BiWorkspace.tsx` — referência de como adicionar tabs a uma página existente
|
|
||||||
- `docker/litellm-config.yaml` — llama3.1:8b já configurado como Tier 2
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Scope Boundary
|
|
||||||
|
|
||||||
**Incluído nesta fase:**
|
|
||||||
- PatternDetector backend (cron + análise de skill_executions)
|
|
||||||
- Geração de DRAFT via AI
|
|
||||||
- Widget de notificação + slide-in
|
|
||||||
- Tab "Sugestões" em /skills com aprovação/rejeição
|
|
||||||
|
|
||||||
**Fora de escopo (phases futuras):**
|
|
||||||
- Dev Center completo (Phase 6)
|
|
||||||
- Detecção de padrões em outras fontes além de skill_executions
|
|
||||||
- Pattern sharing entre tenants
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Plan Breakdown Suggested
|
|
||||||
|
|
||||||
- **04-01:** PatternDetector service (backend) — cron, análise, gravação em detected_patterns + skill_suggestions + DRAFT creation via AI
|
|
||||||
- **04-02:** API routes + frontend widget (badge + slide-in) + tab "Sugestões" em /skills
|
|
||||||
|
|
@ -1,97 +0,0 @@
|
||||||
# Phase 5 Context — Automation Fabric
|
|
||||||
|
|
||||||
> Generated in --auto mode on 2026-03-26. All decisions auto-selected with recommended defaults.
|
|
||||||
|
|
||||||
## Phase Goal
|
|
||||||
|
|
||||||
Automações unificadas sob runtime único — 5 runtimes tipados substituindo a fragmentação entre XOS (`xos_automations`) e Central (`automations`), com `AutomationCenter.tsx` como visão unificada.
|
|
||||||
|
|
||||||
## Prior Context Applied
|
|
||||||
|
|
||||||
- **Stack Node.js/TypeScript:** runtimes ficam no servidor Express, sem novos microserviços Python
|
|
||||||
- **Backend-first:** API definida antes do frontend
|
|
||||||
- **DB imutável:** tabelas `automations` e `xos_automations` não são alteradas — migração virtual
|
|
||||||
- **Padrão de phases anteriores:** wrappers leves sobre código existente, sem reescritas
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Decisions
|
|
||||||
|
|
||||||
### 1. Estratégia de migração
|
|
||||||
|
|
||||||
**[auto] Unificação virtual — zero migração de dados**
|
|
||||||
|
|
||||||
- Tabelas `automations` e `xos_automations` permanecem intactas
|
|
||||||
- Nova camada `server/automation-fabric/` agrega os dois via API layer
|
|
||||||
- `AutomationCenter.tsx` consome endpoint unificado `/api/automation-fabric/list`
|
|
||||||
- Endpoint retorna rows de ambas as tabelas com campo `source: "central" | "xos"`
|
|
||||||
- Dados existentes: sem risco de perda, sem downtime, sem migration script
|
|
||||||
|
|
||||||
### 2. Os 5 runtimes
|
|
||||||
|
|
||||||
**[auto] Classes TypeScript leves — roteamento para serviços existentes**
|
|
||||||
|
|
||||||
Implementados em `server/automation-fabric/runtimes/`:
|
|
||||||
|
|
||||||
| Runtime | Responsabilidade | Delega para |
|
|
||||||
|---|---|---|
|
|
||||||
| `WorkflowEngine` | Executa ações sequenciais com condições | `AutomationService` existente |
|
|
||||||
| `RuleEngine` | Avalia condições JSONB e dispara ações | Lógica inline, sem dependência |
|
|
||||||
| `AgentExecutor` | Executa automações via agente Manus | `ManusService` |
|
|
||||||
| `ScheduleEngine` | Gerencia cron/interval | `scheduledTasks` + setInterval existente |
|
|
||||||
| `EventEngine` | Webhook/event triggers | Registra listeners no Express |
|
|
||||||
|
|
||||||
- `AutomationFabricService` orquestra: dado tipo de trigger → escolhe runtime correto
|
|
||||||
- Trigger routing: `schedule` → ScheduleEngine, `event`/`webhook` → EventEngine, `agent` → AgentExecutor, default → WorkflowEngine
|
|
||||||
|
|
||||||
### 3. Rota do AutomationCenter
|
|
||||||
|
|
||||||
**[auto] Nova rota `/automations-center` — existentes intocadas**
|
|
||||||
|
|
||||||
- `/automations` (Central) e `/xos/automations` (XOS) permanecem sem alteração
|
|
||||||
- Nova página `client/src/pages/AutomationCenter.tsx` em `/automations-center`
|
|
||||||
- Adicionada ao nav/router sem remover rotas existentes
|
|
||||||
|
|
||||||
### 4. UI do AutomationCenter
|
|
||||||
|
|
||||||
**[auto] Lista unificada com badge de fonte + filtros inline**
|
|
||||||
|
|
||||||
- Lista flat com cards: nome, descrição, trigger type, última execução, status ativo/inativo
|
|
||||||
- Badge colorido: `"Central"` (azul) | `"XOS"` (roxo)
|
|
||||||
- Filtros: por fonte, por status (ativo/inativo), por trigger type
|
|
||||||
- Toggle inline enable/disable via PATCH ao respectivo endpoint original
|
|
||||||
- Execução manual via botão "Executar" → POST `/api/automation-fabric/{id}/run`
|
|
||||||
- Empty state informativo
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Reusable Assets Identified
|
|
||||||
|
|
||||||
- `server/automations/service.ts` — `AutomationService.runAutomation()` (reusar no WorkflowEngine)
|
|
||||||
- `server/automations/routes.ts` — padrão de rota com auth check
|
|
||||||
- `server/manus/service.ts` — `manusService` para AgentExecutor
|
|
||||||
- `shared/schema.ts` — `automations`, `xos_automations`, `automationLogs` já existentes
|
|
||||||
- `client/src/pages/Automations.tsx` — referência de UX/layout para a nova página
|
|
||||||
- `client/src/pages/XosAutomations.tsx` — referência de cards e filtros
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Scope Boundary
|
|
||||||
|
|
||||||
**Incluído nesta fase:**
|
|
||||||
- `server/automation-fabric/` — 5 runtimes + `AutomationFabricService` + rotas unificadas
|
|
||||||
- `GET /api/automation-fabric/list` — lista unificada (central + xos)
|
|
||||||
- `POST /api/automation-fabric/:id/run` — execução via fabric
|
|
||||||
- `AutomationCenter.tsx` — página `/automations-center`
|
|
||||||
|
|
||||||
**Fora de escopo (phases futuras):**
|
|
||||||
- Editor visual de automações (Phase 6 / Dev Center)
|
|
||||||
- Migração física de dados para tabela unificada
|
|
||||||
- Novos tipos de trigger não existentes
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Plan Breakdown Suggested
|
|
||||||
|
|
||||||
- **05-01:** `server/automation-fabric/` — 5 runtimes + `AutomationFabricService` + routes (`/api/automation-fabric`)
|
|
||||||
- **05-02:** `AutomationCenter.tsx` — página unificada `/automations-center` com lista, filtros e execução inline
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
# 06-01 PLAN — Schema + API Agent Defs
|
|
||||||
|
|
||||||
## Tasks
|
|
||||||
|
|
||||||
1. **Add `arcadia_agent_defs` table to `shared/schema.ts`**
|
|
||||||
- Fields: id, tenantId, userId, name, description, spec (jsonb), status, version, lastTaskId, createdAt, updatedAt
|
|
||||||
- Run migration if needed
|
|
||||||
|
|
||||||
2. **Create `server/agent-defs/service.ts`**
|
|
||||||
- `createAgentDef()`, `getAgentDef()`, `listAgentDefs()`, `updateAgentDef()`, `deleteAgentDef()`
|
|
||||||
- `updateStatus()` helper for draft → assembling → ready → deployed
|
|
||||||
- `incrementVersion()` on deploy
|
|
||||||
|
|
||||||
3. **Create `server/agent-defs/routes.ts`**
|
|
||||||
- GET `/api/agent-defs` (list by tenantId)
|
|
||||||
- POST `/api/agent-defs` (create)
|
|
||||||
- PATCH `/api/agent-defs/:id` (update spec/status)
|
|
||||||
- DELETE `/api/agent-defs/:id`
|
|
||||||
- POST `/api/agent-defs/:id/deploy` (status → deployed, version++)
|
|
||||||
- POST `/api/agent-defs/:id/run` (POST to blackboard task)
|
|
||||||
|
|
||||||
4. **Register routes in `server/routes.ts`**
|
|
||||||
- Import and registerAgentDefRoutes()
|
|
||||||
|
|
||||||
## Reuse
|
|
||||||
- DB functions from existing patterns
|
|
||||||
- Blackboard service for task creation (no modification)
|
|
||||||
|
|
||||||
## Done Criteria
|
|
||||||
- API responds 200 on all endpoints
|
|
||||||
- Status transitions work: draft → assembling → ready → deployed
|
|
||||||
- `lastTaskId` links to blackboard_tasks
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
# 06-02 PLAN — Design + Assemble Tabs
|
|
||||||
|
|
||||||
## Tasks
|
|
||||||
|
|
||||||
1. **Extend `client/src/pages/DevCenter.tsx` — Design tab**
|
|
||||||
- Add "Design" tab to TabsList
|
|
||||||
- 3 mode buttons: Markdown Spec | Code Editor | Visual Flow
|
|
||||||
- Monaco Editor component (reuse existing from "Desenvolver" tab if possible)
|
|
||||||
- Save spec to `arcadia_agent_defs` on blur/button click
|
|
||||||
- Form: agent name, description, spec content
|
|
||||||
|
|
||||||
2. **Extend `client/src/pages/DevCenter.tsx` — Assemble tab**
|
|
||||||
- List `arcadia_agent_defs` where status = draft
|
|
||||||
- "Montar" button per agent → POST `/api/blackboard/task` with spec as context
|
|
||||||
- Poll `/api/blackboard/task/:id` every 3s while assembling
|
|
||||||
- Show progress (task status) in real-time
|
|
||||||
- On complete: update agent status → ready, show generated artifact
|
|
||||||
|
|
||||||
3. **Create `client/src/components/DesignStudio.tsx`** (if modularizing)
|
|
||||||
- Or inline in DevCenter tabs
|
|
||||||
|
|
||||||
4. **Create `client/src/components/AssembleLine.tsx`** (if modularizing)
|
|
||||||
- Or inline in DevCenter tabs
|
|
||||||
|
|
||||||
## Reuse
|
|
||||||
- Monaco Editor config from existing code
|
|
||||||
- Polling logic from existing task components
|
|
||||||
- Blackboard task fetch logic
|
|
||||||
|
|
||||||
## Done Criteria
|
|
||||||
- Design tab allows editing agent spec in 3 modes
|
|
||||||
- Assemble tab shows draft agents and polls until complete
|
|
||||||
- Status updates in real-time
|
|
||||||
- Artifacts visible after assembly
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
# 06-03 PLAN — Deploy + Galeria Tabs
|
|
||||||
|
|
||||||
## Tasks
|
|
||||||
|
|
||||||
1. **Extend `client/src/pages/DevCenter.tsx` — Deploy tab**
|
|
||||||
- List `arcadia_agent_defs` where status = ready OR deployed
|
|
||||||
- "Deploy" button → PATCH `/api/agent-defs/:id/deploy` (status → deployed, version++)
|
|
||||||
- "Re-montar" button → PATCH status back to draft
|
|
||||||
- Expand row → show last blackboard_artifact details (JSON viewer)
|
|
||||||
- Version badge per agent
|
|
||||||
|
|
||||||
2. **Extend `client/src/pages/DevCenter.tsx` — Galeria tab**
|
|
||||||
- Grid of cards: deployed agents only
|
|
||||||
- Card content: name, description, version, creator, status badge
|
|
||||||
- "Executar" button → POST `/api/agent-defs/:id/run` (new blackboard task)
|
|
||||||
- "Fork" button → POST create new agent with copied spec (name += " (Copy)")
|
|
||||||
- Filter: status dropdown, search by name
|
|
||||||
- Empty state message
|
|
||||||
|
|
||||||
3. **Create `client/src/components/OrchestrateCenter.tsx`** (if modularizing)
|
|
||||||
- Or inline in DevCenter tabs
|
|
||||||
|
|
||||||
4. **Create `client/src/components/AgentGallery.tsx`** (if modularizing)
|
|
||||||
- Or inline in DevCenter tabs
|
|
||||||
|
|
||||||
## Reuse
|
|
||||||
- Card/grid layout from AutomationCenter or Skills
|
|
||||||
- Status badge from existing components
|
|
||||||
- Blackboard task execution logic
|
|
||||||
|
|
||||||
## Done Criteria
|
|
||||||
- Deploy tab lists ready/deployed agents
|
|
||||||
- Deploy action increments version
|
|
||||||
- Re-montar reverts to draft
|
|
||||||
- Galeria shows deployed agents only
|
|
||||||
- Run/Fork actions work
|
|
||||||
- All filters functional
|
|
||||||
|
|
@ -1,131 +0,0 @@
|
||||||
# Phase 6 Context — Dev Center Completo
|
|
||||||
|
|
||||||
> Generated in --auto mode on 2026-03-26. All decisions auto-selected with recommended defaults.
|
|
||||||
|
|
||||||
## Phase Goal
|
|
||||||
|
|
||||||
Fábrica completa de agentes integrada ao DevCenter existente: Design → Assemble → Deploy, com galeria de agentes por tenant.
|
|
||||||
|
|
||||||
## Prior Context Applied
|
|
||||||
|
|
||||||
- **Stack Node.js/TypeScript + React:** sem novos microserviços
|
|
||||||
- **Backend-first:** tabela + API antes do frontend
|
|
||||||
- **Blackboard já existe:** `server/blackboard/` com ArchitectAgent, GeneratorAgent, ValidatorAgent, ExecutorAgent — reusar sem modificar
|
|
||||||
- **DevCenter.tsx já existe em `/dev-center`:** adicionar tabs, não criar nova rota
|
|
||||||
- **Schema modular:** nova tabela pode ir em `shared/schema.ts` (é a fase certa — tabela de agentes faz parte do core do sistema)
|
|
||||||
- **Auditoria imutável:** execuções e artefatos já logados via `blackboard_artifacts`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Decisions
|
|
||||||
|
|
||||||
### 1. Onde vive a nova UI?
|
|
||||||
|
|
||||||
**[auto] 4 novos tabs no DevCenter.tsx existente — sem nova rota**
|
|
||||||
|
|
||||||
- Tabs adicionados ao `<TabsList>` existente em `/dev-center`
|
|
||||||
- Tabs novos: `design`, `assemble`, `deploy`, `galeria`
|
|
||||||
- Tabs existentes (Desenvolver, Status, Analisar Repos, Ferramentas, Sistema, Preview, Histórico) permanecem intocados
|
|
||||||
- Nenhuma nova rota no App.tsx, nenhum novo import lazy
|
|
||||||
|
|
||||||
### 2. DesignStudio — modos do editor
|
|
||||||
|
|
||||||
**[auto] 3 modos: Markdown Spec, Code Editor, Visual Flow**
|
|
||||||
|
|
||||||
- **Markdown Spec** (padrão): textarea com Monaco em modo `markdown` — usuário descreve o agente em linguagem natural estruturada
|
|
||||||
- **Code Editor**: Monaco em modo `typescript` — para specs técnicas detalhadas ou código direto
|
|
||||||
- **Visual Flow**: lista de nós editáveis (JSON simples) — nome/tipo/conexões — NÃO um editor gráfico completo
|
|
||||||
- UML = descrito via texto em Markdown Spec (sem biblioteca de diagramas)
|
|
||||||
- Seletor de modo: 3 botões inline no topo do tab Design
|
|
||||||
|
|
||||||
### 3. Tabela de definições de agentes
|
|
||||||
|
|
||||||
**[auto] Nova tabela `arcadia_agent_defs` em `shared/schema.ts`**
|
|
||||||
|
|
||||||
Campos:
|
|
||||||
- `id` (serial PK)
|
|
||||||
- `tenantId` (varchar, nullable)
|
|
||||||
- `userId` (varchar — criador)
|
|
||||||
- `name` (varchar — nome do agente)
|
|
||||||
- `description` (text)
|
|
||||||
- `spec` (jsonb — conteúdo do DesignStudio: `{ mode, content }`)
|
|
||||||
- `status` (varchar — `draft | assembling | ready | deployed`)
|
|
||||||
- `version` (integer, default 1)
|
|
||||||
- `lastTaskId` (integer — FK para blackboard_tasks, nullable)
|
|
||||||
- `createdAt`, `updatedAt` (timestamp)
|
|
||||||
|
|
||||||
API: `/api/agent-defs` — CRUD padrão (GET list, POST create, PATCH update, DELETE)
|
|
||||||
|
|
||||||
### 4. AssembleLine — fluxo de montagem
|
|
||||||
|
|
||||||
**[auto] DesignStudio spec → POST /api/blackboard/task → GeneratorAgent → artefato linkado**
|
|
||||||
|
|
||||||
- Tab "Assemble" lista `arcadia_agent_defs` com status `draft`
|
|
||||||
- Botão "Montar" por agente: POST `/api/blackboard/task` com spec como contexto
|
|
||||||
- `title`: `"Montar agente: ${name}"`
|
|
||||||
- `description`: conteúdo do spec
|
|
||||||
- `context`: `{ agentDefId, spec, source: "agent-factory" }`
|
|
||||||
- Atualiza `arcadia_agent_defs.status` → `assembling`, salva `lastTaskId`
|
|
||||||
- Progresso: polling de `/api/blackboard/task/:id` a cada 3s
|
|
||||||
- Ao completar: status → `ready`, artefato gerado visível no painel de detalhes
|
|
||||||
- Reusar completamente os agentes Blackboard existentes — zero modificação
|
|
||||||
|
|
||||||
### 5. OrchestrateCenter — deploy e monitoramento
|
|
||||||
|
|
||||||
**[auto] Lista de agent_defs com ações de deploy + viewer de artefatos**
|
|
||||||
|
|
||||||
- Tab "Deploy": lista `arcadia_agent_defs` em status `ready` ou `deployed`
|
|
||||||
- Ação "Deploy": PATCH status → `deployed`, incrementa `version`
|
|
||||||
- Ação "Re-montar": volta para `draft`, decrementa version (permite iteração)
|
|
||||||
- Monitoramento: expandir linha → mostra último `blackboard_artifact` do `lastTaskId`
|
|
||||||
- Versionamento: campo `version` incrementado a cada novo deploy
|
|
||||||
- Sem infra externa — deploy = mudança de status no banco
|
|
||||||
|
|
||||||
### 6. Galeria de agentes
|
|
||||||
|
|
||||||
**[auto] Grid de cards por tenant — tab "Galeria"**
|
|
||||||
|
|
||||||
- Tab "Galeria": grid de cards com `arcadia_agent_defs` onde `status = "deployed"`
|
|
||||||
- Cada card: nome, descrição, badge de status, número de versão, criador
|
|
||||||
- Ação "Executar": POST `/api/blackboard/task` com spec do agente → cria nova task de execução
|
|
||||||
- Ação "Fork": duplica o agente def como novo draft (copia spec, incrementa nome)
|
|
||||||
- Filtro por status (todos / somente deployed) e busca por nome
|
|
||||||
- Empty state: "Nenhum agente implantado — crie um na aba Design"
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Reusable Assets Identified
|
|
||||||
|
|
||||||
- `server/blackboard/routes.ts` — `POST /api/blackboard/task`, `GET /api/blackboard/task/:id`, `GET /api/blackboard/tasks` — reusar sem modificar
|
|
||||||
- `server/blackboard/service.ts` — `createMainTask()`, `getTaskWithDetails()` — reusar
|
|
||||||
- `shared/schema.ts` — `blackboardTasks`, `blackboardArtifacts` — leitura e join
|
|
||||||
- `client/src/pages/DevCenter.tsx` — estrutura de tabs existente — adicionar sem remover
|
|
||||||
- `client/src/pages/AutomationCenter.tsx` — referência de UX: lista com filtros + badges
|
|
||||||
- `client/src/pages/Skills.tsx` — referência de galeria com cards
|
|
||||||
- Monaco Editor já instalado (usado no DevCenter tab "Desenvolver")
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Scope Boundary
|
|
||||||
|
|
||||||
**Incluído nesta fase:**
|
|
||||||
- Tabela `arcadia_agent_defs` + migration + API CRUD `/api/agent-defs`
|
|
||||||
- Tab "Design" (DesignStudio: 3 modos + Monaco)
|
|
||||||
- Tab "Assemble" (lista de defs + botão Montar + progresso polling)
|
|
||||||
- Tab "Deploy" (OrchestrateCenter: lista ready/deployed + ações deploy/re-montar + viewer)
|
|
||||||
- Tab "Galeria" (grid por tenant + run + fork)
|
|
||||||
- Backend endpoint `POST /api/agent-defs/:id/deploy` e `POST /api/agent-defs/:id/run`
|
|
||||||
|
|
||||||
**Fora de escopo (phases futuras):**
|
|
||||||
- Editor gráfico visual com React Flow ou similar
|
|
||||||
- Execução real do código gerado em sandbox (Phase 7)
|
|
||||||
- Integração com CI/CD ou containers reais
|
|
||||||
- Sharing de agentes entre tenants
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Plan Breakdown Suggested
|
|
||||||
|
|
||||||
- **06-01:** `shared/schema.ts` (tabela `arcadia_agent_defs`) + `server/agent-defs/routes.ts` (CRUD + `/deploy` + `/run`) + registro em `server/routes.ts`
|
|
||||||
- **06-02:** Tab "Design" (DesignStudio 3 modos) + Tab "Assemble" (lista + polling de progresso) no DevCenter.tsx
|
|
||||||
- **06-03:** Tab "Deploy" (OrchestrateCenter) + Tab "Galeria" (grid + run/fork) no DevCenter.tsx
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
# Quick Task 260326-kmz: Implementar Skill Fabric
|
|
||||||
|
|
||||||
**Description:** Implementar Skill Fabric — gerador e gestor de skills da Arcádia Suite
|
|
||||||
**Date:** 2026-03-26
|
|
||||||
**Mode:** quick
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Task 1: Backend — Skill Fabric Module
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- `server/modules/skill-fabric/src/types/index.ts`
|
|
||||||
- `server/modules/skill-fabric/src/core/skill/Skill.entity.ts`
|
|
||||||
- `server/modules/skill-fabric/src/core/compiler/VisualCompiler.ts`
|
|
||||||
- `server/modules/skill-fabric/src/core/compiler/CodeCompiler.ts`
|
|
||||||
- `server/modules/skill-fabric/src/core/compiler/MarkdownCompiler.ts`
|
|
||||||
- `server/modules/skill-fabric/src/core/validator/ValidationPipeline.ts`
|
|
||||||
- `server/modules/skill-fabric/src/core/executor/SkillExecutor.ts`
|
|
||||||
- `server/modules/skill-fabric/src/core/executor/Sandbox.ts`
|
|
||||||
- `server/modules/skill-fabric/src/core/lifecycle/LifecycleManager.ts`
|
|
||||||
- `server/modules/skill-fabric/src/api/routes/skills.routes.ts`
|
|
||||||
- `server/modules/skill-fabric/src/api/controllers/skills.controller.ts`
|
|
||||||
- `server/modules/skill-fabric/index.ts`
|
|
||||||
|
|
||||||
**Action:** Create the full backend Skill Fabric module with types, entity, compilers, validator pipeline, executor/sandbox, lifecycle manager, and API routes. The project uses TypeScript + Express + Drizzle ORM. Reuse existing `db` and `skills` schema from `../../shared/schema`. No new DB migration needed — extend existing skills table logic.
|
|
||||||
|
|
||||||
**Context:**
|
|
||||||
- Existing `server/skills/routes.ts` has basic CRUD. The new module adds compilation, validation, execution, versioning, lifecycle.
|
|
||||||
- DB import: `import { db } from "@/db"` or `import { db } from "../../../db"` — check what works
|
|
||||||
- Existing schema: `skills`, `skillExecutions` from `../../shared/schema`
|
|
||||||
- Do NOT use vm2 (needs install) — use Node.js built-in `vm` module for sandbox
|
|
||||||
- Do NOT use ReactFlow/Monaco yet (frontend task) — backend only here
|
|
||||||
|
|
||||||
**Done:** Files created, module compiles without errors
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Task 2: Frontend — Skill Fabric UI
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- `client/src/modules/skill-fabric/index.ts`
|
|
||||||
- `client/src/modules/skill-fabric/types.ts`
|
|
||||||
- `client/src/modules/skill-fabric/api.ts`
|
|
||||||
- `client/src/modules/skill-fabric/code-ide/components/SkillCodeEditor.tsx`
|
|
||||||
- `client/src/modules/skill-fabric/markdown-studio/components/MarkdownEditor.tsx`
|
|
||||||
- `client/src/modules/skill-fabric/shared/components/SkillToolbar.tsx`
|
|
||||||
- `client/src/modules/skill-fabric/shared/components/VersionSelector.tsx`
|
|
||||||
- `client/src/modules/skill-fabric/shared/components/ValidationPanel.tsx`
|
|
||||||
- `client/src/pages/SkillFabricPage.tsx`
|
|
||||||
|
|
||||||
**Action:** Create frontend Skill Fabric module. Use textarea-based code editor (Monaco not installed — don't add it). Use Tailwind CSS + shadcn/ui components matching the existing project style. Create a SkillFabricPage that lists skills, allows creating/editing (code + markdown modes), compiling, validating, and executing. Wire to the backend API routes from Task 1.
|
|
||||||
|
|
||||||
**Context:**
|
|
||||||
- Check `client/src/components/` for existing UI patterns (shadcn components available)
|
|
||||||
- Check `client/src/App.tsx` to see how routes are registered — add the new page there
|
|
||||||
- The visual canvas (ReactFlow) is a stretch goal — skip it, implement code + markdown modes only
|
|
||||||
- API base: `/api/skills` (existing) + `/api/skill-fabric` (new from Task 1)
|
|
||||||
|
|
||||||
**Done:** SkillFabricPage renders, skills can be listed and created
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Task 3: Register Routes & Integration
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- `server/modules/loader.ts`
|
|
||||||
- `client/src/App.tsx` (or router file)
|
|
||||||
|
|
||||||
**Action:** Register the new skill-fabric backend routes in `server/modules/loader.ts`. Add the SkillFabricPage route in the frontend router. Verify existing skills route still works.
|
|
||||||
|
|
||||||
**Done:** `/api/skill-fabric/*` routes registered, frontend page accessible
|
|
||||||
|
|
@ -1,91 +0,0 @@
|
||||||
# GSD Session Report
|
|
||||||
|
|
||||||
**Generated:** 2026-03-25 09:50 BRT
|
|
||||||
**Project:** Arcádia Suite — `arcadiasuite` (branch `Servidor`)
|
|
||||||
**Milestone:** Arcádia Agentic Suite — 6 Fases (~12 semanas)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Session Summary
|
|
||||||
|
|
||||||
**Período:** 2026-03-24 11:30 → 2026-03-25 09:50 (sessões consecutivas)
|
|
||||||
**Fase atual:** Fase 2 — Skills Engine
|
|
||||||
**Commits nesta sessão:** 7
|
|
||||||
**Arquivos alterados:** 29 (+1.875 / -106 linhas)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Work Performed
|
|
||||||
|
|
||||||
### Fase 1 — Fundação (CONCLUÍDA)
|
|
||||||
|
|
||||||
- `1c6c386` — SkillEngine POO + ReferenceParser + ReferenceResolver + schema (`arcadia_skills`, `skill_executions`)
|
|
||||||
- `66d8dfb` — MiroFlow e OpenClaw adicionados como git submodules
|
|
||||||
- `c9a0563` — Neo4j no Docker Compose (profile `kg`), Skills.tsx UI, melhorias Manus/chat/Agent
|
|
||||||
|
|
||||||
### Fase 2 — Skills Engine (EM ANDAMENTO)
|
|
||||||
|
|
||||||
- `3c61acf` — XOS: forms de cadastro restaurados (Novo Contato, Negócio, Atividade)
|
|
||||||
- `9bfc888` — Migration: suporte a upload RAR, SQL, SQL.GZ
|
|
||||||
- `194a8dc` — Rota `/skills` no App.tsx + autocomplete de referências `/` no Monaco
|
|
||||||
- `4057821` — Skill Marketplace (Biblioteca): `GET /api/skills/marketplace` + `POST /import` + UI grid
|
|
||||||
|
|
||||||
### Key Outcomes
|
|
||||||
|
|
||||||
- **SkillEngine** com herança POO (`extends`), composição via referências `/`, audit hash SHA-256
|
|
||||||
- **API REST** completa: CRUD + execute + histórico em `/api/skills`
|
|
||||||
- **Editor Monaco** com 3 tabs (Geral / Body / Params) + autocomplete de 9 prefixos de referências
|
|
||||||
- **Skill Marketplace** (Biblioteca): discovery de skills `system`, importação para `tenant` com clonagem + `extends` para origem
|
|
||||||
- **Neo4j** disponível via `docker compose --profile kg up`
|
|
||||||
- **Submodules** MiroFlow e OpenClaw presentes em `server/modules/`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Files Changed (resumo por área)
|
|
||||||
|
|
||||||
| Área | Arquivos | Linhas |
|
|
||||||
|------|----------|--------|
|
|
||||||
| Skills (server) | `engine.ts`, `routes.ts`, `reference-parser.ts` | +400 |
|
|
||||||
| Skills (client) | `Skills.tsx` | +570 |
|
|
||||||
| Schema | `shared/schema.ts` | +96 |
|
|
||||||
| Infra | `docker-compose.yml`, `docker/litellm-config.yaml` | +35 |
|
|
||||||
| Manus/Chat | `service.ts`, `routes.ts`, `prompt.ts`, `routes.ts` | +350 |
|
|
||||||
| XOS | `XosCentral.tsx` | +200 |
|
|
||||||
| App | `App.tsx` | +2 |
|
|
||||||
| Outros | Agent, ProcessCompass, bi, compass, ide, etc. | +222 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Blockers & Open Items
|
|
||||||
|
|
||||||
**Fase 2 — pendente:**
|
|
||||||
- [ ] Sistema de versionamento de skills (Git-like)
|
|
||||||
|
|
||||||
**Pendências gerais (produção):**
|
|
||||||
- [ ] Commitar RLS Superset → main
|
|
||||||
- [ ] SOE.tsx stub → importar versão completa do Replit
|
|
||||||
- [ ] Seed de regras padrão em `soe_regras`
|
|
||||||
- [ ] Dashboards base no Superset (DRE, Fluxo de Caixa, Fiscal, RH)
|
|
||||||
|
|
||||||
**Próxima fase:**
|
|
||||||
- Fase 3 — MiroFlow embutido + MiroFlowBridge para Superset
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Estimated Resource Usage
|
|
||||||
|
|
||||||
| Métrica | Estimativa |
|
|
||||||
|---------|------------|
|
|
||||||
| Commits | 7 |
|
|
||||||
| Arquivos alterados | 29 |
|
|
||||||
| Linhas adicionadas | ~1.875 |
|
|
||||||
| Linhas removidas | ~106 |
|
|
||||||
| Ciclos plan/execute | ~4 (uma por feature principal) |
|
|
||||||
| Subagents spawned | 0 |
|
|
||||||
|
|
||||||
> **Nota:** contagens de tokens e custo requerem instrumentação via API.
|
|
||||||
> Estas métricas refletem apenas a atividade observável da sessão.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Gerado por `/gsd:session-report`*
|
|
||||||
|
|
@ -1,812 +0,0 @@
|
||||||
# Arcádia Tenants — Mapa Completo da Gestão Multi-Tenant
|
|
||||||
|
|
||||||
> Mapa da arquitetura multi-tenant da Arcádia Suite: hierarquia
|
|
||||||
> Master/Parceiro/Cliente, sistema de planos, ativação de módulos,
|
|
||||||
> empresas (Matriz/Filiais), comissões de parceiros, controle de acesso,
|
|
||||||
> e middleware de isolamento.
|
|
||||||
> Atualizado em: Março 2026
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. Visão Geral
|
|
||||||
|
|
||||||
A Arcádia Suite opera em modelo **multi-tenant hierárquico** com três níveis. Cada tenant possui um plano que define quais módulos da plataforma estão habilitados, e pode conter múltiplas empresas (CNPJ) para operações fiscais.
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────────────┐
|
|
||||||
│ HIERARQUIA DE TENANTS │
|
|
||||||
│ │
|
|
||||||
│ ┌──────────────┐ │
|
|
||||||
│ │ MASTER │ ← Operador da plataforma │
|
|
||||||
│ │ │ ← Vê tudo, controla tudo │
|
|
||||||
│ │ tenantType: │ ← Pode criar parceiros │
|
|
||||||
│ │ "master" │ ← Pode excluir tenants │
|
|
||||||
│ └──────┬───────┘ │
|
|
||||||
│ │ │
|
|
||||||
│ ┌────────────┼────────────┐ │
|
|
||||||
│ ▼ ▼ ▼ │
|
|
||||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
|
||||||
│ │ PARCEIRO A │ │ PARCEIRO B │ │ PARCEIRO C │ │
|
|
||||||
│ │ │ │ │ │ │ │
|
|
||||||
│ │ tenantType: │ │ Consultorias │ │ Integradores │ │
|
|
||||||
│ │ "partner" │ │ Revendas │ │ Franquias │ │
|
|
||||||
│ │ │ │ │ │ │ │
|
|
||||||
│ │ partnerCode: │ │ comissão: % │ │ plano: │ │
|
|
||||||
│ │ "PARC-001" │ │ sobre clientes│ │partner_pro │ │
|
|
||||||
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
|
|
||||||
│ │ │ │ │
|
|
||||||
│ ┌────┴────┐ ┌───┴───┐ ┌───┴───┐ │
|
|
||||||
│ ▼ ▼ ▼ ▼ ▼ ▼ │
|
|
||||||
│ ┌────────┐┌────────┐┌────────┐┌────────┐┌────────┐┌────────┐ │
|
|
||||||
│ │Cliente1││Cliente2││Cliente3││Cliente4││Cliente5││Cliente6│ │
|
|
||||||
│ │ ││ ││ ││ ││ ││ │ │
|
|
||||||
│ │tenant ││tenant ││tenant ││tenant ││tenant ││tenant │ │
|
|
||||||
│ │Type: ││Type: ││Type: ││Type: ││Type: ││Type: │ │
|
|
||||||
│ │"client"││"client"││"client"││"client"││"client"││"client"│ │
|
|
||||||
│ │ ││ ││ ││ ││ ││ │ │
|
|
||||||
│ │parent: ││parent: ││parent: ││parent: ││parent: ││parent: │ │
|
|
||||||
│ │Parc.A ││Parc.A ││Parc.B ││Parc.B ││Parc.C ││Parc.C │ │
|
|
||||||
│ │ ││ ││ ││ ││ ││ │ │
|
|
||||||
│ │Plano: ││Plano: ││Plano: ││Plano: ││Plano: ││Plano: │ │
|
|
||||||
│ │starter ││pro ││free ││enterpr.││starter ││pro │ │
|
|
||||||
│ └────────┘└────────┘└────────┘└────────┘└────────┘└────────┘ │
|
|
||||||
└─────────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### Visibilidade por tipo:
|
|
||||||
|
|
||||||
| Tipo | Vê | Cria | Exclui |
|
|
||||||
|------|-----|------|--------|
|
|
||||||
| **Master** | Todos os tenants | Parceiros e Clientes | Qualquer (exceto master) |
|
|
||||||
| **Partner** | Seu tenant + seus clientes | Apenas clientes vinculados | Nenhum |
|
|
||||||
| **Client** | Apenas seu próprio tenant | Nenhum | Nenhum |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Banco de Dados — Tabelas de Tenant
|
|
||||||
|
|
||||||
### 2.1 — tenants (Tabela Principal)
|
|
||||||
|
|
||||||
```
|
|
||||||
┌──────────────────────────────────────────────────────────────────┐
|
|
||||||
│ tenants │
|
|
||||||
│ Tabela: "tenants" — shared/schema.ts:849 │
|
|
||||||
│ │
|
|
||||||
│ ┌─────────── Identificação ──────────────────┐ │
|
|
||||||
│ │ id serial PK │ │
|
|
||||||
│ │ name text NOT NULL │ │
|
|
||||||
│ │ slug text UNIQUE │ │
|
|
||||||
│ │ email text │ │
|
|
||||||
│ │ phone text │ │
|
|
||||||
│ │ logoUrl text │ │
|
|
||||||
│ └─────────────────────────────────────────────┘ │
|
|
||||||
│ │
|
|
||||||
│ ┌─────────── Hierarquia ─────────────────────┐ │
|
|
||||||
│ │ tenantType "master" | "partner" | │ │
|
|
||||||
│ │ "client" │ │
|
|
||||||
│ │ parentTenantId integer (self-ref FK) │ │
|
|
||||||
│ │ partnerCode text (código único parceiro) │ │
|
|
||||||
│ │ commissionRate numeric(5,2) (% comissão) │ │
|
|
||||||
│ └─────────────────────────────────────────────┘ │
|
|
||||||
│ │
|
|
||||||
│ ┌─────────── Plano e Limites ────────────────┐ │
|
|
||||||
│ │ plan "free" | "starter" | "pro" │ │
|
|
||||||
│ │ | "enterprise" | │ │
|
|
||||||
│ │ "partner_starter" | │ │
|
|
||||||
│ │ "partner_pro" │ │
|
|
||||||
│ │ status "active" | "trial" | │ │
|
|
||||||
│ │ "suspended" | "cancelled" │ │
|
|
||||||
│ │ maxUsers integer (default 5) │ │
|
|
||||||
│ │ maxStorageMb integer (default 1000) │ │
|
|
||||||
│ │ features JSONB → TenantFeatures │ ← Módulos │
|
|
||||||
│ └─────────────────────────────────────────────┘ │
|
|
||||||
│ │
|
|
||||||
│ ┌─────────── Billing ────────────────────────┐ │
|
|
||||||
│ │ billingEmail text │ │
|
|
||||||
│ │ trialEndsAt timestamp │ │
|
|
||||||
│ └─────────────────────────────────────────────┘ │
|
|
||||||
│ │
|
|
||||||
│ ┌─────────── Dados Empresariais ─────────────┐ │
|
|
||||||
│ │ cnpj text │ │
|
|
||||||
│ │ tradeName text │ │
|
|
||||||
│ │ address text │ │
|
|
||||||
│ │ city, state text │ │
|
|
||||||
│ │ segment text │ │
|
|
||||||
│ │ notes, source text │ │
|
|
||||||
│ │ commercialContact / commercialPhone │ │
|
|
||||||
│ └─────────────────────────────────────────────┘ │
|
|
||||||
│ │
|
|
||||||
│ ┌─────────── Metadata ───────────────────────┐ │
|
|
||||||
│ │ settings text (JSON config) │ │
|
|
||||||
│ │ createdAt timestamp │ │
|
|
||||||
│ │ updatedAt timestamp │ │
|
|
||||||
│ └─────────────────────────────────────────────┘ │
|
|
||||||
└──────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.2 — TenantFeatures (JSONB — Sistema de Módulos)
|
|
||||||
|
|
||||||
O campo `features` na tabela `tenants` é um JSONB que controla **quais módulos da Arcádia estão habilitados** para cada tenant. O tipo é definido em `shared/schema.ts:821` e espelhado no frontend em `client/src/hooks/use-tenant-features.ts:3`.
|
|
||||||
|
|
||||||
```
|
|
||||||
┌──────────────────────────────────────────────────────────────────┐
|
|
||||||
│ TenantFeatures (JSONB) │
|
|
||||||
│ │
|
|
||||||
│ ┌─────────── Módulos Booleanos ──────────────┐ │
|
|
||||||
│ │ │ │
|
|
||||||
│ │ ide → IDE integrada │ │
|
|
||||||
│ │ whatsapp → WhatsApp multi-sessão │ │
|
|
||||||
│ │ crm → CRM completo │ │
|
|
||||||
│ │ erp → SOE/ERP │ │
|
|
||||||
│ │ bi → Business Intelligence │ │
|
|
||||||
│ │ manus → IA Manus (GPT-4o) │ │
|
|
||||||
│ │ centralApis → APIs centrais │ │
|
|
||||||
│ │ centralApisManage → Gerenciar APIs │ │
|
|
||||||
│ │ comunidades → Chat interno │ │
|
|
||||||
│ │ biblioteca → Biblioteca de conhecimento │ │
|
|
||||||
│ │ bibliotecaPublish → Publicar na biblioteca │ │
|
|
||||||
│ │ suporteN3 → Suporte nível 3 │ │
|
|
||||||
│ │ retail → Módulo Varejo │ │
|
|
||||||
│ │ plus → ERP Plus (Laravel) │ │
|
|
||||||
│ │ fisco → Motor Fiscal │ │
|
|
||||||
│ │ cockpit → Cockpit executivo │ │
|
|
||||||
│ │ compass → Process Compass │ │
|
|
||||||
│ │ production → Módulo Produção │ │
|
|
||||||
│ │ support → Módulo Suporte │ │
|
|
||||||
│ │ xosCrm → XOS CRM (Kanban) │ │
|
|
||||||
│ │ │ │
|
|
||||||
│ └─────────────────────────────────────────────┘ │
|
|
||||||
│ │
|
|
||||||
│ ┌─────────── Configurações Numéricas ────────┐ │
|
|
||||||
│ │ │ │
|
|
||||||
│ │ whatsappSessions → Nº sessões WA (0-N) │ │
|
|
||||||
│ │ maxChannels → Nº canais permitidos │ │
|
|
||||||
│ │ │ │
|
|
||||||
│ └─────────────────────────────────────────────┘ │
|
|
||||||
│ │
|
|
||||||
│ ┌─────────── Configurações de Texto ─────────┐ │
|
|
||||||
│ │ │ │
|
|
||||||
│ │ ideMode → "none" | "no-code" | │ │
|
|
||||||
│ │ "low-code" | "pro-code" │ │
|
|
||||||
│ │ manusTools[] → Lista de tools habilitadas │ │
|
|
||||||
│ │ │ │
|
|
||||||
│ └─────────────────────────────────────────────┘ │
|
|
||||||
└──────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.3 — tenant_empresas (Matriz/Filiais)
|
|
||||||
|
|
||||||
Cada tenant pode ter **múltiplas empresas** (CNPJs) cadastradas — essencial para operações fiscais (NF-e/NFC-e) por filial.
|
|
||||||
|
|
||||||
```
|
|
||||||
┌──────────────────────────────────────────────────────────────────┐
|
|
||||||
│ tenant_empresas │
|
|
||||||
│ Tabela: "tenant_empresas" — shared/schema.ts:889 │
|
|
||||||
│ │
|
|
||||||
│ ┌─────────── Identificação ──────────────────┐ │
|
|
||||||
│ │ id serial PK │ │
|
|
||||||
│ │ tenantId FK → tenants.id (CASCADE) │ │
|
|
||||||
│ │ razaoSocial text NOT NULL │ │
|
|
||||||
│ │ nomeFantasia text │ │
|
|
||||||
│ │ cnpj text NOT NULL │ │
|
|
||||||
│ │ ie text (inscrição estadual) │ │
|
|
||||||
│ │ im text (inscrição municipal) │ │
|
|
||||||
│ │ email, phone text │ │
|
|
||||||
│ │ tipo "matriz" | "filial" │ │
|
|
||||||
│ │ status "active" | "inactive" │ │
|
|
||||||
│ └─────────────────────────────────────────────┘ │
|
|
||||||
│ │
|
|
||||||
│ ┌─────────── Endereço ───────────────────────┐ │
|
|
||||||
│ │ cep, logradouro, numero, complemento, │ │
|
|
||||||
│ │ bairro, cidade, uf, codigoIbge │ │
|
|
||||||
│ └─────────────────────────────────────────────┘ │
|
|
||||||
│ │
|
|
||||||
│ ┌─────────── Fiscal ─────────────────────────┐ │
|
|
||||||
│ │ regimeTributario "simples" | "presumido" │ │
|
|
||||||
│ │ | "real" │ │
|
|
||||||
│ │ certificadoDigitalId FK certificado │ │
|
|
||||||
│ │ ambienteFiscal "producao" | "homologacao" │ │
|
|
||||||
│ │ serieNfe integer (default 1) │ │
|
|
||||||
│ │ serieNfce integer (default 1) │ │
|
|
||||||
│ │ plusEmpresaId integer (link ERP Plus) │ │
|
|
||||||
│ └─────────────────────────────────────────────┘ │
|
|
||||||
│ │
|
|
||||||
│ ┌─────────── Metadata ───────────────────────┐ │
|
|
||||||
│ │ createdAt, updatedAt timestamp │ │
|
|
||||||
│ └─────────────────────────────────────────────┘ │
|
|
||||||
└──────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
**Uso típico:**
|
|
||||||
|
|
||||||
```
|
|
||||||
Tenant "Empresa ABC" (id: 5)
|
|
||||||
├── Empresa Matriz: "ABC Comércio LTDA" (CNPJ: 12.345.678/0001-00)
|
|
||||||
│ regime: simples, ambiente: producao, série NF-e: 1
|
|
||||||
├── Filial SP: "ABC Comércio SP" (CNPJ: 12.345.678/0002-81)
|
|
||||||
│ regime: simples, ambiente: producao, série NF-e: 2
|
|
||||||
└── Filial RJ: "ABC Comércio RJ" (CNPJ: 12.345.678/0003-62)
|
|
||||||
regime: simples, ambiente: homologacao, série NF-e: 1
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.4 — tenant_users (Membership)
|
|
||||||
|
|
||||||
```
|
|
||||||
┌──────────────────────────────────────────────────────────────────┐
|
|
||||||
│ tenant_users │
|
|
||||||
│ Tabela: "tenant_users" — shared/schema.ts:923 │
|
|
||||||
│ │
|
|
||||||
│ id serial PK │
|
|
||||||
│ tenantId FK → tenants.id (CASCADE) │
|
|
||||||
│ userId FK → users.id (CASCADE) │
|
|
||||||
│ role "owner" | "admin" | "member" │
|
|
||||||
│ isOwner "true" | "false" │
|
|
||||||
│ createdAt timestamp │
|
|
||||||
│ │
|
|
||||||
│ → Link N:N entre users e tenants │
|
|
||||||
│ → Um usuário pode pertencer a múltiplos tenants │
|
|
||||||
│ → Cada tenant tem um owner + admins + members │
|
|
||||||
└──────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.5 — tenant_plans (Planos Disponíveis)
|
|
||||||
|
|
||||||
```
|
|
||||||
┌──────────────────────────────────────────────────────────────────┐
|
|
||||||
│ tenant_plans │
|
|
||||||
│ Tabela: "tenant_plans" — shared/schema.ts:933 │
|
|
||||||
│ │
|
|
||||||
│ id serial PK │
|
|
||||||
│ code text UNIQUE ("free", "starter", "pro", │
|
|
||||||
│ "enterprise", "partner_starter", "partner_pro") │
|
|
||||||
│ name text NOT NULL │
|
|
||||||
│ description text │
|
|
||||||
│ tenantType "master" | "partner" | "client" │
|
|
||||||
│ maxUsers integer (default 5) │
|
|
||||||
│ maxStorageMb integer (default 1000) │
|
|
||||||
│ features JSONB → TenantFeatures │
|
|
||||||
│ monthlyPrice integer (centavos) │
|
|
||||||
│ yearlyPrice integer (centavos) │
|
|
||||||
│ trialDays integer (default 14) │
|
|
||||||
│ isActive "true" | "false" │
|
|
||||||
│ sortOrder integer │
|
|
||||||
│ createdAt timestamp │
|
|
||||||
└──────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.6 — partner_clients (Relacionamento Parceiro↔Cliente)
|
|
||||||
|
|
||||||
```
|
|
||||||
┌──────────────────────────────────────────────────────────────────┐
|
|
||||||
│ partner_clients │
|
|
||||||
│ Tabela: "partner_clients" — shared/schema.ts:951 │
|
|
||||||
│ │
|
|
||||||
│ id serial PK │
|
|
||||||
│ partnerId FK → tenants.id (CASCADE) │
|
|
||||||
│ clientId FK → tenants.id (CASCADE) │
|
|
||||||
│ commissionRate numeric(5,2) — Override do rate do parceiro │
|
|
||||||
│ status "active" | "suspended" | "ended" │
|
|
||||||
│ notes text │
|
|
||||||
│ startedAt timestamp │
|
|
||||||
│ endedAt timestamp │
|
|
||||||
└──────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.7 — partner_commissions (Comissões dos Parceiros)
|
|
||||||
|
|
||||||
```
|
|
||||||
┌──────────────────────────────────────────────────────────────────┐
|
|
||||||
│ partner_commissions │
|
|
||||||
│ Tabela: "partner_commissions" — shared/schema.ts:963 │
|
|
||||||
│ │
|
|
||||||
│ id serial PK │
|
|
||||||
│ partnerId FK → tenants.id (CASCADE) │
|
|
||||||
│ clientId FK → tenants.id (CASCADE) │
|
|
||||||
│ referenceMonth text ("2026-01", "2026-02", ...) │
|
|
||||||
│ clientPlanCode text (código do plano do cliente) │
|
|
||||||
│ clientPlanValue integer (valor do plano em centavos) │
|
|
||||||
│ commissionRate numeric(5,2) NOT NULL │
|
|
||||||
│ commissionValue integer NOT NULL (valor em centavos) │
|
|
||||||
│ status "pending" | "approved" | "paid" | "cancelled" │
|
|
||||||
│ approvedAt timestamp │
|
|
||||||
│ paidAt timestamp │
|
|
||||||
│ paymentReference text │
|
|
||||||
│ createdAt timestamp │
|
|
||||||
│ │
|
|
||||||
│ Fluxo: pending → approved → paid │
|
|
||||||
│ │
|
|
||||||
│ Exemplo: │
|
|
||||||
│ Parceiro A tem Cliente X no plano "pro" (R$ 299/mês) │
|
|
||||||
│ commissionRate: 20% │
|
|
||||||
│ commissionValue: 5980 centavos (R$ 59,80) │
|
|
||||||
└──────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Planos Pré-definidos (Seed)
|
|
||||||
|
|
||||||
A rota `POST /api/admin/plans/seed` cria os 6 planos padrão:
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────────────┐
|
|
||||||
│ PLANOS PARA CLIENTES │
|
|
||||||
├────────────┬──────────┬──────────┬──────────────────────────────────┤
|
|
||||||
│ Plano │ Preço/mês│ Usuários │ Módulos │
|
|
||||||
├────────────┼──────────┼──────────┼──────────────────────────────────┤
|
|
||||||
│ Gratuito │ R$ 0 │ 2 │ erp, crm │
|
|
||||||
│ (free) │ │ │ │
|
|
||||||
├────────────┼──────────┼──────────┼──────────────────────────────────┤
|
|
||||||
│ Starter │ R$ 99 │ 5 │ erp, crm, bi, fisco, │
|
|
||||||
│ │ │ │ whatsapp, compass │
|
|
||||||
├────────────┼──────────┼──────────┼──────────────────────────────────┤
|
|
||||||
│ Pro │ R$ 299 │ 15 │ erp, crm, bi, fisco, retail, │
|
|
||||||
│ │ │ │ plus, whatsapp, manus, cockpit, │
|
|
||||||
│ │ │ │ compass, support, biblioteca │
|
|
||||||
├────────────┼──────────┼──────────┼──────────────────────────────────┤
|
|
||||||
│ Enterprise │ R$ 999 │ 100 │ TODOS os módulos │
|
|
||||||
│ │ │ 50GB │ (erp, crm, bi, fisco, retail, │
|
|
||||||
│ │ │ │ plus, whatsapp, manus, ide, │
|
|
||||||
│ │ │ │ cockpit, compass, production, │
|
|
||||||
│ │ │ │ support, xosCrm, centralApis, │
|
|
||||||
│ │ │ │ comunidades, biblioteca) │
|
|
||||||
└────────────┴──────────┴──────────┴──────────────────────────────────┘
|
|
||||||
|
|
||||||
┌─────────────────────────────────────────────────────────────────────┐
|
|
||||||
│ PLANOS PARA PARCEIROS │
|
|
||||||
├──────────────────┬──────────┬──────────┬────────────────────────────┤
|
|
||||||
│ Plano │ Preço/mês│ Usuários │ Módulos │
|
|
||||||
├──────────────────┼──────────┼──────────┼────────────────────────────┤
|
|
||||||
│ Parceiro Starter │ R$ 199 │ 10 │ erp, crm, bi, fisco, │
|
|
||||||
│ │ │ │ retail, whatsapp, compass, │
|
|
||||||
│ │ │ │ support │
|
|
||||||
├──────────────────┼──────────┼──────────┼────────────────────────────┤
|
|
||||||
│ Parceiro Pro │ R$ 499 │ 50 │ TODOS os módulos │
|
|
||||||
│ │ │ 25GB │ (igual Enterprise) │
|
|
||||||
└──────────────────┴──────────┴──────────┴────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Sistema de Papéis (Roles)
|
|
||||||
|
|
||||||
A Arcádia opera com **dois níveis de papéis** que se complementam:
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────────────┐
|
|
||||||
│ SISTEMA DUPLO DE ROLES │
|
|
||||||
│ │
|
|
||||||
│ ┌─────────── Papel do Sistema (users.role) ──────────────────┐ │
|
|
||||||
│ │ │ │
|
|
||||||
│ │ "master" → Superadmin, acesso total, bypass de tenant │ │
|
|
||||||
│ │ "admin" → Administrador, acessa /api/admin/* │ │
|
|
||||||
│ │ "user" → Usuário padrão, sem acesso admin │ │
|
|
||||||
│ │ │ │
|
|
||||||
│ │ Verificado no middleware global de admin: │ │
|
|
||||||
│ │ if (role !== "admin" && role !== "master") → 403 │ │
|
|
||||||
│ └─────────────────────────────────────────────────────────────┘ │
|
|
||||||
│ │
|
|
||||||
│ ┌─────────── Papel no Tenant (tenant_users.role) ────────────┐ │
|
|
||||||
│ │ │ │
|
|
||||||
│ │ "owner" → Dono do tenant, criador, full control │ │
|
|
||||||
│ │ "admin" → Administrador do tenant │ │
|
|
||||||
│ │ "member" → Membro comum, acesso operacional │ │
|
|
||||||
│ │ │ │
|
|
||||||
│ │ Usado para scoping de dados e permissões intra-tenant │ │
|
|
||||||
│ └─────────────────────────────────────────────────────────────┘ │
|
|
||||||
│ │
|
|
||||||
│ Combinação no login (getEnrichedUser): │
|
|
||||||
│ user.role + user.tenantId + user.tenantType + user.tenantRole │
|
|
||||||
└─────────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Controle de Acesso — Helpers
|
|
||||||
|
|
||||||
O backend implementa dois helpers essenciais em `server/admin/routes.ts`:
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────────────┐
|
|
||||||
│ getAllowedTenantIds(user) │
|
|
||||||
│ │
|
|
||||||
│ Retorna os IDs de tenants que o usuário pode acessar: │
|
|
||||||
│ │
|
|
||||||
│ • master → null (acesso total, sem filtro) │
|
|
||||||
│ • partner → [seu tenantId, ...clientIds vinculados] │
|
|
||||||
│ • client → [apenas seu tenantId] │
|
|
||||||
│ │
|
|
||||||
│ Usado em: GET /tenants, GET /partner-clients, GET /commissions, │
|
|
||||||
│ GET /empresas, GET /tenants/hierarchy │
|
|
||||||
└─────────────────────────────────────────────────────────────────────┘
|
|
||||||
|
|
||||||
┌─────────────────────────────────────────────────────────────────────┐
|
|
||||||
│ canAccessTenant(user, tenantId) │
|
|
||||||
│ │
|
|
||||||
│ Verifica se o usuário pode operar sobre um tenant específico: │
|
|
||||||
│ │
|
|
||||||
│ • master → true (sempre) │
|
|
||||||
│ • partner → true se tenantId ∈ getAllowedTenantIds() │
|
|
||||||
│ • client → true apenas se tenantId === user.tenantId │
|
|
||||||
│ │
|
|
||||||
│ Usado em: PATCH /tenants/:id, DELETE /empresas/:id, │
|
|
||||||
│ POST /empresas, etc. │
|
|
||||||
└─────────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. Middleware de Módulos (requireModule)
|
|
||||||
|
|
||||||
O middleware `requireModule` em `server/retail/routes.ts:25` bloqueia endpoints quando um módulo não está ativo para o tenant:
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────────────┐
|
|
||||||
│ requireModule(moduleKey: keyof TenantFeatures) │
|
|
||||||
│ │
|
|
||||||
│ Fluxo: │
|
|
||||||
│ 1. Identifica tenantId do usuário logado │
|
|
||||||
│ 2. Busca tenant no banco │
|
|
||||||
│ 3. Verifica features[moduleKey] === true │
|
|
||||||
│ 4. Se false → 403 com mensagem: │
|
|
||||||
│ { │
|
|
||||||
│ error: "Módulo 'X' não está ativo para este tenant", │
|
|
||||||
│ moduleKey: "plus", │
|
|
||||||
│ action: "Ative o módulo em Admin → Módulos" │
|
|
||||||
│ } │
|
|
||||||
│ │
|
|
||||||
│ Exemplo de uso: │
|
|
||||||
│ router.post("/plus/sync/customers", │
|
|
||||||
│ requireModule("plus"), ← Bloqueia se Plus não está ativo │
|
|
||||||
│ async (req, res) => { ... } │
|
|
||||||
│ ); │
|
|
||||||
└─────────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. Hook Frontend (useTenantFeatures)
|
|
||||||
|
|
||||||
O hook `useTenantFeatures()` em `client/src/hooks/use-tenant-features.ts` fornece acesso às features do tenant no frontend:
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────────────┐
|
|
||||||
│ useTenantFeatures() │
|
|
||||||
│ │
|
|
||||||
│ Fonte: GET /api/soe/tenant/modules │
|
|
||||||
│ Cache: 60 segundos (staleTime: 60000) │
|
|
||||||
│ │
|
|
||||||
│ Retorna: │
|
|
||||||
│ { │
|
|
||||||
│ features: TenantFeatures, ← Objeto completo de features │
|
|
||||||
│ isLoading: boolean, │
|
|
||||||
│ plan: string, ← Código do plano atual │
|
|
||||||
│ isEnabled: (key) => bool, ← Verificação rápida │
|
|
||||||
│ } │
|
|
||||||
│ │
|
|
||||||
│ Defaults (quando não logado / sem tenant): │
|
|
||||||
│ ide: true, crm: true, erp: true, manus: true, compass: true │
|
|
||||||
│ Todos os demais: false │
|
|
||||||
│ │
|
|
||||||
│ Uso no componente: │
|
|
||||||
│ const { isEnabled } = useTenantFeatures(); │
|
|
||||||
│ if (isEnabled("bi")) { /* mostra menu BI */ } │
|
|
||||||
│ if (isEnabled("retail")) { /* mostra módulo varejo */ } │
|
|
||||||
└─────────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. Interface de Administração
|
|
||||||
|
|
||||||
### 8.1 — Admin.tsx (Página Principal)
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────────────┐
|
|
||||||
│ Admin.tsx — client/src/pages/Admin.tsx (2.635 linhas) │
|
|
||||||
│ Rota: /admin │
|
|
||||||
│ │
|
|
||||||
│ 6 Abas: │
|
|
||||||
│ ┌─────────────┬──────────┬────────────┬───────────┬──────────────┐ │
|
|
||||||
│ │ Conexões │ Tarefas │ Knowledge │ Bibliotecas│ Módulos │ │
|
|
||||||
│ │ │ │ │ │ │ │
|
|
||||||
│ │ WhatsApp │ Dev │ Knowledge │ Conteúdo │ Ativação │ │
|
|
||||||
│ │ ERPNext │ Center │ Graph │ Docs │ por tenant │ │
|
|
||||||
│ │ APIs │ Pipeline │ Q&A │ Templates │ Toggle on/ │ │
|
|
||||||
│ │ │ │ │ │ off módulos │ │
|
|
||||||
│ └─────────────┴──────────┴────────────┴───────────┴──────────────┘ │
|
|
||||||
│ ┌─────────────┐ │
|
|
||||||
│ │Casa de │ │
|
|
||||||
│ │Máquinas │ │
|
|
||||||
│ │ │ │
|
|
||||||
│ │ Status dos │ │
|
|
||||||
│ │ motores │ │
|
|
||||||
│ │ (:8002-8006)│ │
|
|
||||||
│ └─────────────┘ │
|
|
||||||
│ │
|
|
||||||
│ Aba "Módulos" renderiza: <MultiTenantSection /> │
|
|
||||||
└─────────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### 8.2 — MultiTenantSection (Gestão Completa)
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────────────┐
|
|
||||||
│ MultiTenantSection.tsx (1.486 linhas) │
|
|
||||||
│ client/src/components/MultiTenantSection.tsx │
|
|
||||||
│ │
|
|
||||||
│ 4 Abas internas: │
|
|
||||||
│ │
|
|
||||||
│ ┌─ Tenants ──────────────────────────────────────────────────┐ │
|
|
||||||
│ │ │ │
|
|
||||||
│ │ Lista de tenants com: │ │
|
|
||||||
│ │ • Nome, tipo (Master/Parceiro/Cliente) │ │
|
|
||||||
│ │ • Plano atual e status │ │
|
|
||||||
│ │ • Nº de filhos (childCount) │ │
|
|
||||||
│ │ • Tenant pai (parentTenant) │ │
|
|
||||||
│ │ • Ações: Editar, Ativar/Desativar módulos │ │
|
|
||||||
│ │ │ │
|
|
||||||
│ │ Formulário de criação: │ │
|
|
||||||
│ │ • Nome, email, phone │ │
|
|
||||||
│ │ • Tipo (master/partner/client) │ │
|
|
||||||
│ │ • Plano (seleciona do tenant_plans) │ │
|
|
||||||
│ │ • Tenant pai (para hierarquia) │ │
|
|
||||||
│ │ • Features auto-preenchidas pelo plano selecionado │ │
|
|
||||||
│ │ • Toggle individual de cada módulo │ │
|
|
||||||
│ │ │ │
|
|
||||||
│ └─────────────────────────────────────────────────────────────┘ │
|
|
||||||
│ │
|
|
||||||
│ ┌─ Planos ───────────────────────────────────────────────────┐ │
|
|
||||||
│ │ │ │
|
|
||||||
│ │ CRUD de planos com: │ │
|
|
||||||
│ │ • Código, nome, descrição │ │
|
|
||||||
│ │ • Tipo de tenant (client/partner) │ │
|
|
||||||
│ │ • Preço mensal e anual (centavos) │ │
|
|
||||||
│ │ • Max usuários e storage │ │
|
|
||||||
│ │ • Features (toggle de cada módulo) │ │
|
|
||||||
│ │ • Botão "Seed" para criar planos padrão │ │
|
|
||||||
│ │ • Botão "Propagar" para aplicar features do plano │ │
|
|
||||||
│ │ a todos os tenants que usam esse plano │ │
|
|
||||||
│ │ │ │
|
|
||||||
│ └─────────────────────────────────────────────────────────────┘ │
|
|
||||||
│ │
|
|
||||||
│ ┌─ Comissões ────────────────────────────────────────────────┐ │
|
|
||||||
│ │ │ │
|
|
||||||
│ │ Lista de comissões de parceiros: │ │
|
|
||||||
│ │ • Parceiro, Cliente, Mês referência │ │
|
|
||||||
│ │ • Valor do plano, taxa de comissão, valor da comissão │ │
|
|
||||||
│ │ • Status (pending → approved → paid) │ │
|
|
||||||
│ │ • Ações: Aprovar, Marcar como paga │ │
|
|
||||||
│ │ │ │
|
|
||||||
│ └─────────────────────────────────────────────────────────────┘ │
|
|
||||||
│ │
|
|
||||||
│ ┌─ Empresas ─────────────────────────────────────────────────┐ │
|
|
||||||
│ │ │ │
|
|
||||||
│ │ EmpresasSubSection: │ │
|
|
||||||
│ │ • Lista de empresas (Matriz/Filiais) por tenant │ │
|
|
||||||
│ │ • CNPJ, Razão Social, Nome Fantasia │ │
|
|
||||||
│ │ • IE, IM, Regime Tributário │ │
|
|
||||||
│ │ • Endereço completo (CEP, logradouro, cidade, UF) │ │
|
|
||||||
│ │ • Config Fiscal (ambiente, série NF-e, série NFC-e) │ │
|
|
||||||
│ │ • Link com ERP Plus (plusEmpresaId) │ │
|
|
||||||
│ │ • Ações: Criar, Editar, Excluir │ │
|
|
||||||
│ │ │ │
|
|
||||||
│ └─────────────────────────────────────────────────────────────┘ │
|
|
||||||
└─────────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 9. API REST Completa (/api/admin/*)
|
|
||||||
|
|
||||||
### Tenants
|
|
||||||
|
|
||||||
| Método | Endpoint | Função | Acesso |
|
|
||||||
|--------|----------|--------|--------|
|
|
||||||
| GET | `/tenants` | Lista tenants (filtrado por acesso) | master/partner/client |
|
|
||||||
| POST | `/tenants` | Cria tenant (resolve features do plano) | master/admin |
|
|
||||||
| PATCH | `/tenants/:id` | Atualiza tenant (canAccessTenant) | master/partner |
|
|
||||||
| DELETE | `/tenants/:id` | Exclui tenant (proteção master) | somente master |
|
|
||||||
| GET | `/tenants/hierarchy` | Árvore hierárquica de tenants | master/partner |
|
|
||||||
| GET | `/tenants/stats` | Estatísticas (total, por tipo, por status) | master/admin |
|
|
||||||
|
|
||||||
### Planos
|
|
||||||
|
|
||||||
| Método | Endpoint | Função |
|
|
||||||
|--------|----------|--------|
|
|
||||||
| GET | `/plans` | Lista todos os planos |
|
|
||||||
| POST | `/plans` | Cria novo plano |
|
|
||||||
| PATCH | `/plans/:id` | Atualiza plano |
|
|
||||||
| DELETE | `/plans/:id` | Exclui plano |
|
|
||||||
| POST | `/plans/:id/propagate` | Propaga features do plano para todos os tenants que o usam |
|
|
||||||
| POST | `/plans/seed` | Cria 6 planos padrão |
|
|
||||||
|
|
||||||
### Parceiro-Cliente
|
|
||||||
|
|
||||||
| Método | Endpoint | Função |
|
|
||||||
|--------|----------|--------|
|
|
||||||
| GET | `/partner-clients` | Lista relacionamentos parceiro↔cliente |
|
|
||||||
| POST | `/partner-clients` | Cria vínculo parceiro↔cliente |
|
|
||||||
|
|
||||||
### Comissões de Parceiros
|
|
||||||
|
|
||||||
| Método | Endpoint | Função |
|
|
||||||
|--------|----------|--------|
|
|
||||||
| GET | `/commissions` | Lista comissões (filtro por partnerId, status) |
|
|
||||||
| PATCH | `/commissions/:id/approve` | Aprova comissão |
|
|
||||||
| PATCH | `/commissions/:id/pay` | Marca como paga |
|
|
||||||
|
|
||||||
### Empresas (Matriz/Filiais)
|
|
||||||
|
|
||||||
| Método | Endpoint | Função |
|
|
||||||
|--------|----------|--------|
|
|
||||||
| GET | `/empresas` | Lista empresas (filtro por tenantId + acesso) |
|
|
||||||
| GET | `/empresas/:id` | Detalhe da empresa |
|
|
||||||
| POST | `/empresas` | Cria empresa (verifica canAccessTenant) |
|
|
||||||
| PATCH | `/empresas/:id` | Atualiza empresa |
|
|
||||||
| DELETE | `/empresas/:id` | Exclui empresa |
|
|
||||||
|
|
||||||
### Outros Admin
|
|
||||||
|
|
||||||
| Método | Endpoint | Função |
|
|
||||||
|--------|----------|--------|
|
|
||||||
| GET | `/profiles` | Lista perfis de usuários |
|
|
||||||
| POST | `/profiles` | Cria perfil |
|
|
||||||
| PATCH | `/profiles/:id` | Atualiza perfil |
|
|
||||||
| DELETE | `/profiles/:id` | Exclui perfil |
|
|
||||||
| GET | `/users` | Lista usuários (com tenant info) |
|
|
||||||
| PATCH | `/users/:id` | Atualiza usuário |
|
|
||||||
| PATCH | `/users/:id/status` | Altera status do usuário |
|
|
||||||
| PATCH | `/partners/:id/status` | Altera status de parceiro |
|
|
||||||
| PATCH | `/clients/:id/status` | Altera status de cliente |
|
|
||||||
| GET | `/stats` | Estatísticas gerais (tenants, users, leads) |
|
|
||||||
| GET | `/libraries` | Lista bibliotecas de conteúdo |
|
|
||||||
|
|
||||||
**Total: 33 endpoints de administração**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 10. Fluxo Completo — Onboarding de Tenant
|
|
||||||
|
|
||||||
```
|
|
||||||
┌──────────┐ ┌────────────┐ ┌──────────────┐ ┌──────────────┐
|
|
||||||
│ 1. CRIAR │───▶│ 2. PLANO │───▶│ 3. FEATURES │───▶│ 4. EMPRESAS │
|
|
||||||
│ TENANT │ │ │ │ │ │ │
|
|
||||||
│ │ │ Seleciona │ │ Auto-resolve │ │ Cadastra │
|
|
||||||
│ POST │ │ plano │ │ do plano ou │ │ Matriz │
|
|
||||||
│ /admin/ │ │ (free, │ │ toggle │ │ + Filiais │
|
|
||||||
│ tenants │ │ starter, │ │ individual │ │ │
|
|
||||||
│ │ │ pro, etc) │ │ de módulos │ │ CNPJ, IE, │
|
|
||||||
│ name, │ │ │ │ │ │ endereço, │
|
|
||||||
│ email, │ │ maxUsers │ │ ide: true │ │ regime │
|
|
||||||
│ type │ │ maxStorage │ │ crm: true │ │ tributário, │
|
|
||||||
│ │ │ preço │ │ bi: false │ │ certificado │
|
|
||||||
│ │ │ │ │ ... │ │ digital │
|
|
||||||
└──────────┘ └────────────┘ └──────────────┘ └──────────────┘
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐
|
|
||||||
│ 5. VINCULAR │ │ 6. COMISSÕES │ │ 7. USUÁRIOS │
|
|
||||||
│ PARCEIRO │ │ │ │ │
|
|
||||||
│ (se partner) │ │ Automáticas │ │ Convida users │
|
|
||||||
│ │ │ mensais │ │ tenant_users │
|
|
||||||
│ POST /admin/ │ │ baseadas no │ │ roles: owner/admin/ │
|
|
||||||
│ partner- │ │ plano do │ │ member │
|
|
||||||
│ clients │ │ cliente │ │ │
|
|
||||||
│ │ │ │ │ Scoping automático │
|
|
||||||
│ partnerId │ │ pending → │ │ de todos os dados │
|
|
||||||
│ clientId │ │ approved → │ │ por tenantId │
|
|
||||||
│ commission% │ │ paid │ │ │
|
|
||||||
└──────────────┘ └──────────────┘ └──────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 11. Propagação de Plano
|
|
||||||
|
|
||||||
Quando um plano é atualizado, a feature de **propagação** atualiza todos os tenants que usam esse plano:
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────────────┐
|
|
||||||
│ POST /api/admin/plans/:id/propagate │
|
|
||||||
│ │
|
|
||||||
│ 1. Busca o plano por ID │
|
|
||||||
│ 2. Pega features, maxUsers, maxStorageMb do plano │
|
|
||||||
│ 3. Atualiza TODOS os tenants onde plan === plan.code: │
|
|
||||||
│ UPDATE tenants SET │
|
|
||||||
│ features = plan.features, │
|
|
||||||
│ maxUsers = plan.maxUsers, │
|
|
||||||
│ maxStorageMb = plan.maxStorageMb │
|
|
||||||
│ WHERE plan = 'pro' │
|
|
||||||
│ 4. Retorna: { propagated: 15, planCode: "pro" } │
|
|
||||||
│ │
|
|
||||||
│ Exemplo: │
|
|
||||||
│ Plano "pro" ganha módulo "production" → │
|
|
||||||
│ Propagar → 15 tenants "pro" recebem production: true │
|
|
||||||
└─────────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 12. Isolamento de Dados
|
|
||||||
|
|
||||||
Toda query de dados respeita o escopo do tenant:
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────────────┐
|
|
||||||
│ ISOLAMENTO POR tenantId │
|
|
||||||
│ │
|
|
||||||
│ Padrão em todas as queries: │
|
|
||||||
│ │
|
|
||||||
│ const tenantId = (req.user as any).tenantId; │
|
|
||||||
│ const data = await db.select() │
|
|
||||||
│ .from(tabela) │
|
|
||||||
│ .where(eq(tabela.tenantId, tenantId)); │
|
|
||||||
│ │
|
|
||||||
│ Tabelas com tenantId: │
|
|
||||||
│ • tenants (hierarquia) • crm_channels │
|
|
||||||
│ • tenant_empresas • crm_threads │
|
|
||||||
│ • crm_leads • crm_events │
|
|
||||||
│ • crm_clients • crm_campaigns │
|
|
||||||
│ • crm_opportunities • crm_products │
|
|
||||||
│ • crm_partners • crm_contracts │
|
|
||||||
│ • crm_pipeline_stages • crm_commission_rules │
|
|
||||||
│ • crm_proposals • crm_quick_messages │
|
|
||||||
│ • retail_activity_feed • valuation_* │
|
|
||||||
│ │
|
|
||||||
│ Resultado: Dados do Tenant A NUNCA são vistos pelo Tenant B │
|
|
||||||
└─────────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 13. Arquivos-Chave
|
|
||||||
|
|
||||||
| Arquivo | Função | Linhas |
|
|
||||||
|---------|--------|--------|
|
|
||||||
| `shared/schema.ts` (L821-977) | TenantFeatures type + 6 tabelas tenant | ~160 |
|
|
||||||
| `server/admin/routes.ts` | API admin (33 endpoints) | 876 |
|
|
||||||
| `client/src/pages/Admin.tsx` | Página admin (6 abas) | 2.635 |
|
|
||||||
| `client/src/components/MultiTenantSection.tsx` | Gestão multi-tenant (4 sub-abas) | 1.486 |
|
|
||||||
| `client/src/hooks/use-tenant-features.ts` | Hook de features no frontend | 59 |
|
|
||||||
| `server/retail/routes.ts` (L25) | Middleware requireModule | ~25 |
|
|
||||||
| `server/storage.ts` (getEnrichedUser) | Enriquece user com tenant info | — |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 14. Resumo Visual
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────────────┐
|
|
||||||
│ ARCÁDIA MULTI-TENANT STACK │
|
|
||||||
│ │
|
|
||||||
│ ┌───── Autenticação ──────────────────────────────────────────┐ │
|
|
||||||
│ │ Login → getEnrichedUser → user + tenantId + tenantType │ │
|
|
||||||
│ │ + tenantRole + features │ │
|
|
||||||
│ └─────────────────────────────────────────┬───────────────────┘ │
|
|
||||||
│ │ │
|
|
||||||
│ ┌─────────────────────────────────────────▼───────────────────┐ │
|
|
||||||
│ │ Middleware │ │
|
|
||||||
│ │ ┌──────────┐ ┌─────────────┐ ┌────────────────────┐ │ │
|
|
||||||
│ │ │requireAuth│ │requireModule │ │getAllowedTenantIds │ │ │
|
|
||||||
│ │ │role check │ │feature check │ │canAccessTenant │ │ │
|
|
||||||
│ │ └──────────┘ └─────────────┘ └────────────────────┘ │ │
|
|
||||||
│ └─────────────────────────────────────────┬───────────────────┘ │
|
|
||||||
│ │ │
|
|
||||||
│ ┌─────────────────────────────────────────▼───────────────────┐ │
|
|
||||||
│ │ API /api/admin/* │ │
|
|
||||||
│ │ 33 endpoints: Tenants, Plans, Empresas, Comissões │ │
|
|
||||||
│ └─────────────────────────────────────────┬───────────────────┘ │
|
|
||||||
│ │ │
|
|
||||||
│ ┌─────────────────────────────────────────▼───────────────────┐ │
|
|
||||||
│ │ PostgreSQL │ │
|
|
||||||
│ │ 6 tabelas: tenants, tenant_users, tenant_plans, │ │
|
|
||||||
│ │ tenant_empresas, partner_clients, partner_commissions │ │
|
|
||||||
│ └─────────────────────────────────────────────────────────────┘ │
|
|
||||||
│ │
|
|
||||||
│ ┌───── Frontend ──────────────────────────────────────────────┐ │
|
|
||||||
│ │ Admin.tsx (6 abas) → MultiTenantSection (4 sub-abas) │ │
|
|
||||||
│ │ useTenantFeatures() hook → GET /api/soe/tenant/modules │ │
|
|
||||||
│ └─────────────────────────────────────────────────────────────┘ │
|
|
||||||
└─────────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
@ -1,167 +0,0 @@
|
||||||
# Sessão de Desenvolvimento — 2026-03-18
|
|
||||||
|
|
||||||
**Branch:** `claude/analyze-project-0mXjP`
|
|
||||||
**Commits desta sessão:** `8d14fc0`, `20a0237`
|
|
||||||
**Contexto:** Continuação do ciclo iniciado em 2026-03-13 (auditoria A-Z + desenvolvimento das fases XOS)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. O que foi feito nesta sessão
|
|
||||||
|
|
||||||
### 1.1 Correção de erros TypeScript críticos no `App.tsx`
|
|
||||||
|
|
||||||
Três classes de erros impediam o build limpo do frontend:
|
|
||||||
|
|
||||||
| Erro | Arquivo | Correção |
|
|
||||||
|---|---|---|
|
|
||||||
| `Cannot find module '@/contexts/SoeMotorContext'` | `App.tsx:8` | Criado `client/src/contexts/SoeMotorContext.tsx` |
|
|
||||||
| `Cannot find module '@/pages/SOE'` | `App.tsx:39` | Criado `client/src/pages/SOE.tsx` |
|
|
||||||
| `LazyExoticComponent` não atribuível a `() => Element` (20x) | `protected-route.tsx` | Tipo alterado para `React.ComponentType<any>` |
|
|
||||||
|
|
||||||
**Arquivos criados/modificados:**
|
|
||||||
- `client/src/contexts/SoeMotorContext.tsx` — context global com `SoeMotorProvider` e hook `useSoeMotor`
|
|
||||||
- `client/src/pages/SOE.tsx` — página SOE (Sistema de Operações Empresariais), re-export do módulo Plus/ERP
|
|
||||||
- `client/src/lib/protected-route.tsx` — tipo do prop `component` corrigido
|
|
||||||
|
|
||||||
**Resultado:** `npx tsc --noEmit` zerou todos os erros do `App.tsx`. Erros remanescentes são pré-existentes em outros arquivos e não relacionados às correções.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 1.2 Guia de deploy no Coolify (`DEPLOY_COOLIFY.md`)
|
|
||||||
|
|
||||||
Documento completo de 359 linhas cobrindo:
|
|
||||||
|
|
||||||
- **Arquitetura de serviços** — mapa visual de todos os containers e redes
|
|
||||||
- **Variáveis de ambiente** — obrigatórias e opcionais por módulo (Core, IA, Superset, Plus, ERPNext)
|
|
||||||
- **Profiles Docker** — como habilitar `ai`, `bi`, `plus`, `erpnext` via `COMPOSE_PROFILES`
|
|
||||||
- **Sequência de inicialização** — ordem garantida pelos `depends_on` e healthchecks
|
|
||||||
- **Traefik + HTTPS** — labels prontas para Let's Encrypt via Coolify
|
|
||||||
- **Volumes e persistência** — tabela de criticidade + comandos de backup PostgreSQL
|
|
||||||
- **Stack de IA** — 3 tiers LiteLLM (LLMFit → Ollama → externos), como baixar modelos, como habilitar LLMFit
|
|
||||||
- **Pós-deploy** — migrations, verificação de saúde
|
|
||||||
- **Checklist final** — antes, durante e após o deploy
|
|
||||||
- **Comandos de referência rápida**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 1.3 Relatório de comparação Auditoria vs Desenvolvimento
|
|
||||||
|
|
||||||
Análise completa cruzando o `ARCADIA_AUDIT_EXECUCAO.md` (auditoria de 13/Mar) com todos os 18 commits do branch:
|
|
||||||
|
|
||||||
**Resultado geral: 44% concluído, 17% parcial, 38% pendente**
|
|
||||||
|
|
||||||
| Sprint | ✅ | ⚠️ | ❌ |
|
|
||||||
|---|---|---|---|
|
|
||||||
| S — Segurança (10 itens) | 7 | 3 | 0 |
|
|
||||||
| 0 — Deploy (6 itens) | 2 | 1 | 3 |
|
|
||||||
| 1 — Inteligência (6 itens) | 2 | 2 | 2 |
|
|
||||||
| 2 — App Store (4 itens) | 3 | 0 | 1 |
|
|
||||||
| 3 — Compass IA (4 itens) | 1 | 1 | 2 |
|
|
||||||
| 4 — ERP Real (6 itens) | 0 | 0 | 6 |
|
|
||||||
| 5 — Soberania IA (4 itens) | 3 | 1 | 0 |
|
|
||||||
| 6 — Qualidade (7 itens) | 3 | 0 | 4 |
|
|
||||||
| 7 — ERPNext (5 itens) | 2 | 1 | 2 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Estado do Superset BI (análise técnica)
|
|
||||||
|
|
||||||
Levantamento completo da integração Apache Superset:
|
|
||||||
|
|
||||||
**Funcionando:**
|
|
||||||
- Gateway Node.js com 4 endpoints (`/guest-token`, `/dashboards`, `/health`, proxy `/superset/*`)
|
|
||||||
- Guest Token JWT via service account com cache de 50 min
|
|
||||||
- Componente React `<SupersetDashboard>` reutilizável com fallback iframe
|
|
||||||
- Docker: init automático, banco `arcadia` registrado somente leitura, admin criado
|
|
||||||
- Feature flags: embed, drill-to-detail, cross-filters, alerts, templates Jinja
|
|
||||||
|
|
||||||
**Pendente / stub:**
|
|
||||||
- RLS por tenant — `rls: []` vazio, todos veem todos os dados
|
|
||||||
- Nenhum dashboard pré-criado — criação manual pelo usuário
|
|
||||||
- Cache Redis — usando `SimpleCache` em memória
|
|
||||||
- SSO/OAuth — auth local apenas
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Contexto acumulado do ciclo completo (desde 13/Mar)
|
|
||||||
|
|
||||||
### O que foi construído no branch (18 commits, 71 arquivos, +9.399 linhas)
|
|
||||||
|
|
||||||
| Área | Principais entregas |
|
|
||||||
|---|---|
|
|
||||||
| **Segurança** | Auth em todas as rotas XOS, CORS Python corrigido, credentials removidas, timeout+retry, approval guard Manus |
|
|
||||||
| **XOS CRM** | Fases 1–4 completas: automações, SLA, horário comercial, campanhas, Socket.IO tempo real |
|
|
||||||
| **Novas páginas React** | `XosSupervisor`, `XosReports`, `XosProtocols` |
|
|
||||||
| **SOE Rule Engine** | Motor de regras contábil/fiscal + importador ERPNext |
|
|
||||||
| **Superset BI** | Integração completa embed + proxy + guest token |
|
|
||||||
| **Infra Docker** | `docker-compose.prod.yml` com profiles: `ai`, `bi`, `plus`, `erpnext` |
|
|
||||||
| **IA Soberania** | LiteLLM 3 tiers + Ollama + slot LLMFit pronto |
|
|
||||||
| **Qualidade** | Rate limiting, logging estruturado (`server/logger.ts`), paginação |
|
|
||||||
| **App Store** | `GET /api/marketplace/my-apps` + AppCenter por subscription |
|
|
||||||
| **Docs** | `DEPLOY_COOLIFY.md`, `INTEGRACAO_IA.md`, `PLANO_BI_SUPERSET.md`, `PLANO_SOE_CENTRAL_v2.md` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Demandas prioritárias pendentes
|
|
||||||
|
|
||||||
### Críticas (bloqueia produção estável)
|
|
||||||
|
|
||||||
1. **LMS e Quality** — auth ainda incompleta (SEC-02, SEC-03)
|
|
||||||
- `server/lms/routes.ts` — 8 rotas ainda públicas
|
|
||||||
- `server/quality/routes.ts` — ~50 rotas revisão insuficiente
|
|
||||||
|
|
||||||
2. **WhatsApp auto-reply** — configuração ainda em `Map` na memória
|
|
||||||
- Reiniciar servidor = perda total da configuração sem aviso
|
|
||||||
- Persistir em tabela `whatsapp_auto_reply_config` (migration já existe)
|
|
||||||
|
|
||||||
3. **`pg_dump` do Replit** — exportar banco antes de encerrar o plano
|
|
||||||
- Risco de perda de dados de produção atual
|
|
||||||
|
|
||||||
### Altas (produto incompleto)
|
|
||||||
|
|
||||||
4. **Ciclo de aprendizado Manus** — embeddings não são populados automaticamente
|
|
||||||
- Busca semântica retorna vazio, sistema não aprende das interações
|
|
||||||
|
|
||||||
5. **Páginas placeholder** — `Agent.tsx`, `CentralApis.tsx`, `ApiHub.tsx`
|
|
||||||
- Três das páginas mais estratégicas ainda sem implementação
|
|
||||||
|
|
||||||
6. **Sprint 4 inteiro — Integrações ERP reais**
|
|
||||||
- Adaptadores Omie e TOTVS: zero implementado
|
|
||||||
- Todos os conectores da Central de API ainda mockados
|
|
||||||
- Estrutura fragmentada em 4 lugares (`server/erpnext/`, `server/crm/frappe-service.ts`, `server/api-central/`, `server/migration/`)
|
|
||||||
|
|
||||||
### Médias (qualidade de engenharia)
|
|
||||||
|
|
||||||
7. **Error boundaries globais no React** — sem captura de erros em cascata
|
|
||||||
8. **Testes automatizados** — zero no projeto inteiro
|
|
||||||
9. **Connection pooling Python** — nova conexão DB por request nos 6 microserviços
|
|
||||||
10. **Consolidar sistemas de comunicação** — legacy (`whatsapp_*`, `chat_*`) e moderno (`comm_*`, `xosConversations`) duplicados
|
|
||||||
11. **36 tabelas sem insert schema** — risco de runtime errors no Drizzle
|
|
||||||
12. **40+ tabelas sem `createdAt`/`updatedAt`** — auditoria e ordenação impossíveis
|
|
||||||
|
|
||||||
### Decisões arquiteturais abertas
|
|
||||||
|
|
||||||
| # | Decisão |
|
|
||||||
|---|---|
|
|
||||||
| D1 | BI padrão: Metabase (existente) vs Superset (integrado)? |
|
|
||||||
| D2 | LMS: tabelas dinâmicas vs schema.ts fixo? |
|
|
||||||
| D3 | Sistema de comunicação canônico: legacy vs moderno vs XOS? |
|
|
||||||
| D4 | Marketplace público ou autenticado (hoje público — intencional)? |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Próxima sessão recomendada
|
|
||||||
|
|
||||||
**Sequência sugerida por impacto/risco:**
|
|
||||||
|
|
||||||
```
|
|
||||||
1. Corrigir auth LMS + Quality (2–3h) → fecha todas as vulnerabilidades SEC
|
|
||||||
2. Persistir WhatsApp auto-reply no banco (1h) → estabilidade em produção
|
|
||||||
3. Fechar ciclo embeddings Manus (2h) → IA começa a aprender de verdade
|
|
||||||
4. Implementar Agent.tsx com UI real (3–4h) → página mais estratégica
|
|
||||||
5. Adaptador Omie — primeira integração ERP real (4–6h)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Sessão registrada em 2026-03-18 | Branch: `claude/analyze-project-0mXjP`*
|
|
||||||
|
|
@ -129,6 +129,7 @@
|
||||||
"@types/connect-pg-simple": "^7.0.3",
|
"@types/connect-pg-simple": "^7.0.3",
|
||||||
"@types/express": "4.17.21",
|
"@types/express": "4.17.21",
|
||||||
"@types/express-session": "^1.18.0",
|
"@types/express-session": "^1.18.0",
|
||||||
|
"@types/http-proxy-middleware": "^0.19.3",
|
||||||
"@types/node": "^20.19.0",
|
"@types/node": "^20.19.0",
|
||||||
"@types/passport": "^1.0.16",
|
"@types/passport": "^1.0.16",
|
||||||
"@types/passport-local": "^1.0.38",
|
"@types/passport-local": "^1.0.38",
|
||||||
|
|
@ -7488,6 +7489,18 @@
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/http-proxy-middleware": {
|
||||||
|
"version": "0.19.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/http-proxy-middleware/-/http-proxy-middleware-0.19.3.tgz",
|
||||||
|
"integrity": "sha512-lnBTx6HCOUeIJMLbI/LaL5EmdKLhczJY5oeXZpX/cXE4rRqb3RmV7VcMpiEfYkmTjipv3h7IAyIINe4plEv7cA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/connect": "*",
|
||||||
|
"@types/http-proxy": "*",
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/imap": {
|
"node_modules/@types/imap": {
|
||||||
"version": "0.8.43",
|
"version": "0.8.43",
|
||||||
"resolved": "https://registry.npmjs.org/@types/imap/-/imap-0.8.43.tgz",
|
"resolved": "https://registry.npmjs.org/@types/imap/-/imap-0.8.43.tgz",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
# BI-API Gateway - Node.js
|
||||||
|
# Arcádia Suite - Port 8004
|
||||||
|
# Roda com tsx (TypeScript runtime)
|
||||||
|
|
||||||
|
FROM node:20-alpine
|
||||||
|
|
||||||
|
LABEL maintainer="Arcádia Suite"
|
||||||
|
LABEL description="BI-API Gateway - Proxy para MetaSet e FDB-Bridge"
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Instalar tsx globalmente
|
||||||
|
RUN npm install -g tsx
|
||||||
|
|
||||||
|
# Criar package.json mínimo com as dependências necessárias
|
||||||
|
RUN npm init -y && \
|
||||||
|
npm install express@4 cors@2 helmet@7 http-proxy-middleware@2 && \
|
||||||
|
npm cache clean --force
|
||||||
|
|
||||||
|
# Copiar apenas o arquivo do gateway
|
||||||
|
COPY server/bi/index.ts ./server/bi/index.ts
|
||||||
|
|
||||||
|
# Variáveis de ambiente padrão
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
ENV PORT=8004
|
||||||
|
ENV METASET_HOST=metaset
|
||||||
|
ENV METASET_PORT=8100
|
||||||
|
ENV FDB_BRIDGE_HOST=fdb-bridge
|
||||||
|
ENV FDB_BRIDGE_PORT=8200
|
||||||
|
|
||||||
|
EXPOSE 8004
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||||
|
CMD wget -qO- http://localhost:8004/health || exit 1
|
||||||
|
|
||||||
|
CMD ["tsx", "server/bi/index.ts"]
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
-- Criar banco de dados MetaSet
|
||||||
|
CREATE DATABASE metaset_db OWNER arcadia;
|
||||||
|
|
||||||
|
-- Comentário
|
||||||
|
COMMENT ON DATABASE metaset_db IS 'MetaSet BI - Apache Superset metadata database';
|
||||||
Loading…
Reference in New Issue