docs: varredura geral, documentação por função e regra de atualização
A documentação descrevia um sistema que não existe mais: 62/73 ferramentas
(são 74), writer.py e models.py como arquivos (viraram pacotes), 1032 testes
(são 1454), models_api.py descrito como "API FastAPI" (é ponte JSON) e o app
SwiftUI ausente por completo — 5.500 linhas que o usuário opera todo dia sem
uma linha de documentação.
Cada arquivo passa a ter uma função específica, com cabeçalho de escopo
dizendo o que cobre e o que NÃO cobre (com a seta para quem cobre). O objetivo
é ler só o necessário: doc fora do assunto custa tempo e processamento sem
entregar nada.
01 arquitetura camadas, duas portas de entrada, regras transversais
02 módulos mapa do engine, incluindo o pipeline de voz
03 server/tools as 74 tools, helpers e como criar uma nova
08 app macOS NOVO — build por swiftc, telas, ponte, etapa 5
09 manutenção NOVO — por onde começar, o que está aberto, sintoma→arquivo
CLAUDE.md ganha a seção "Documentação (MANDATORY)": tabela de roteamento
(qual arquivo abrir para cada tarefa) e a regra de que toda alteração de
código atualiza a doc no mesmo commit, com o mapa de o-que-mexeu → o-que-
atualizar. Doc velha engana mais que doc ausente.
O índice do 05_EXPERIENCIAS subiu para o topo: consultar "isso já quebrou
antes?" custava carregar 1.281 linhas antes de chegar na tabela.
Dívidas levantadas na varredura e registradas em 09 §2: etapa 6 ainda ignora
o phrase_review.json, offset de ~400ms do Whisper, MacApp sem teste, admin/
fora do lint, confirmações visuais pendentes no FCP, submódulo WHISPERX sujo.
Também corrigidos dois links quebrados no Engine/README que apontavam um
nível acima do certo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ffaebb3f72
commit
dcdd73edb5
@@ -1,109 +1,177 @@
|
||||
# 01 — Arquitetura do Sistema (G-ART / fcp-mcp-server)
|
||||
|
||||
> Referência canônica de como o sistema está dividido e implementado. Leia este
|
||||
> documento antes de qualquer mudança de código.
|
||||
> **Escopo:** Como o sistema é dividido em camadas e onde cada responsabilidade mora.
|
||||
> **Não cobre:** Detalhe módulo a módulo (→ 02) · ferramentas MCP (→ 03) · app (→ 08)
|
||||
|
||||
> Referência canônica de como o sistema está dividido. Leia antes de qualquer
|
||||
> mudança de código. Se algo aqui divergir do código, **o código está certo e
|
||||
> este documento está velho** — corrija-o no mesmo commit.
|
||||
|
||||
Última varredura: 2026-08-19 · 74 ferramentas MCP · 1.454 testes · versão `0.6.35`
|
||||
|
||||
---
|
||||
|
||||
## 1. Visão de cima (camadas)
|
||||
|
||||
O sistema é um **servidor MCP em Python** que lê/analisa/reescreve arquivos
|
||||
**FCPXML** do Final Cut Pro. Há **três camadas** bem separadas:
|
||||
O sistema lê, analisa e reescreve **FCPXML** do Final Cut Pro. Ele opera *fora*
|
||||
do FCP: você exporta o XML, o programa processa como dados estruturados e
|
||||
devolve um XML para importar. Nada é patcheado, nenhuma API privada é usada.
|
||||
|
||||
São **quatro camadas**, e o ponto importante é que existem **duas portas de
|
||||
entrada diferentes** para o mesmo motor:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ admin/ — Aplicações complementares (fora do MCP) │
|
||||
│ models_api.py API (FastAPI) p/ gerenciar modelos │
|
||||
│ models_gui.py UI desktop (Flet) p/ gerenciar modelos │
|
||||
│ graphify.sh/.md Pipeline de graphify do código │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ server.py — CAMADA MCP / TRANSPORTE (NÃO tem lógica) │
|
||||
│ 73 tools, handlers, prompts, resources, dispatch │
|
||||
│ Só valida entrada/saída e traduz JSON-RPC → chamadas │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ fcpxml/ — "ENGINE" = NÚCLEO PURO Python (desacoplado) │
|
||||
│ Não conhece MCP nem argumentos de tool. │
|
||||
│ Trabalha com objetos Python e XML. │
|
||||
│ É o foco / onde quase tudo mora. │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
┌──────────────────────────┐ ┌──────────────────────────────┐
|
||||
│ MacApp/ (SwiftUI) │ │ Cliente MCP (Claude) │
|
||||
│ O app que o usuário usa │ │ Conversa, decide a edição │
|
||||
└───────────┬──────────────┘ └───────────────┬──────────────┘
|
||||
│ subprocesso + JSON-lines │ JSON-RPC (stdio)
|
||||
▼ ▼
|
||||
┌──────────────────────────┐ ┌──────────────────────────────┐
|
||||
│ admin/models_api.py │ │ server.py + server_tools/ │
|
||||
│ + admin/api/ │ │ 74 tools, dispatch, schemas │
|
||||
│ 37 comandos da ponte │ │ NÃO tem lógica de timeline │
|
||||
└───────────┬──────────────┘ └───────────────┬──────────────┘
|
||||
└───────────────┬────────────────────┘
|
||||
▼
|
||||
┌───────────────────────────────────┐
|
||||
│ fcpxml/ — O ENGINE │
|
||||
│ Núcleo puro Python, desacoplado. │
|
||||
│ Não conhece MCP nem o app. │
|
||||
│ É onde quase tudo mora. │
|
||||
└───────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Regra de arquitetura:** `server.py` NUNCA implementa lógica de timeline —
|
||||
ele delega ao `fcpxml/`. Tudo em `fcpxml/` é testável isoladamente (1032 testes).
|
||||
**A regra que sustenta tudo:** nem `server.py` nem `admin/api/` implementam
|
||||
lógica de timeline. Os dois validam entrada, chamam o engine e formatam a
|
||||
saída. Toda regra de negócio é testável sem MCP e sem app.
|
||||
|
||||
## 2. Regras transversais (convenções em todo o código)
|
||||
**Por que duas portas.** O MCP existe para o julgamento editorial — qual tomada
|
||||
usar, onde dar zoom — que é conversa com uma IA. A ponte existe para o que o
|
||||
usuário faz sozinho no app — transcrever, configurar, processar. As duas caem
|
||||
no mesmo engine, então uma correção ali vale para as duas.
|
||||
|
||||
---
|
||||
|
||||
## 2. Regras transversais (valem em todo o código)
|
||||
|
||||
| Conceito | Regra |
|
||||
|----------|-------|
|
||||
| **Tempo** | `TimeValue` fração racional `"600/2400s"`. Nunca use float p/ tempo. |
|
||||
| **I/O paths** | Sempre via helpers `_validate_filepath` / `_validate_output_path` (sandbox). |
|
||||
| **Nome de saída** | Nunca sobrescrever original: `output_<suffix>.fcpxml`. |
|
||||
| **Segurança XML** | Sempre `defusedxml` (via `safe_xml.py`). Nunca `xml.etree` direto. |
|
||||
| **Tempo** | `TimeValue`, fração racional `"600/2400s"`. **Nunca float para tempo.** |
|
||||
| **Tempo de decisão** | Ações de voz usam sempre segundos da **mídia original**, nunca pós-corte. |
|
||||
| **I/O paths** | Sempre via `_validate_filepath` / `_validate_output_path` (sandbox). |
|
||||
| **Nome de saída** | Nunca sobrescrever o original: `generate_output_path()` gera `_suffix`. |
|
||||
| **Segurança XML** | Sempre `defusedxml` via `safe_xml.py`. Nunca `xml.etree` direto para ler. |
|
||||
| **Deps opcionais** | `librosa`/`ffmpeg`/`huggingface_hub` importados **lazy**, degradam com `None`. |
|
||||
| **Lint** | `ruff check . --exclude docs/` — zero erros. |
|
||||
| **Validação pós-correção** | `./Engine/run_after_fix.sh` SEMPRE após cada correção. |
|
||||
| **Idioma** | Comunicação com o usuário em português. Código e comentários em inglês. |
|
||||
| **Validação** | `./Engine/run_after_fix.sh` **sempre** após cada correção. |
|
||||
| **App** | Alterou `MacApp/`? Compile e rode: `./MacApp/build_app.sh --run`. |
|
||||
|
||||
## 3. Fluxo de um request (round-trip)
|
||||
---
|
||||
|
||||
## 3. Fluxo de um request
|
||||
|
||||
### Pela porta MCP (Claude decidindo a edição)
|
||||
|
||||
```
|
||||
Cliente MCP (Claude)
|
||||
│ JSON-RPC (stdio)
|
||||
▼
|
||||
server.py ── dispatcher (TOOL_HANDLERS)
|
||||
│ valida path, parseia projeto, chama engine
|
||||
▼
|
||||
fcpxml/parser.py XML → objetos
|
||||
fcpxml/writer.py edita / grava
|
||||
fcpxml/rough_cut.py gera novas timelines
|
||||
fcpxml/export.py cross-NLE
|
||||
▼
|
||||
output_<suffix>.fcpxml (original intocado)
|
||||
▼
|
||||
Final Cut Pro: File → Import → XML (ou push_to_fcp, sem cliques)
|
||||
Cliente MCP ──JSON-RPC──► server.py
|
||||
│ TOOL_HANDLERS[nome]
|
||||
▼
|
||||
server_tools/<categoria>.py
|
||||
│ _shared/: valida path, parseia projeto
|
||||
▼
|
||||
fcpxml/ (parser → writer → safe_xml)
|
||||
▼
|
||||
projeto_<suffix>.fcpxml (original intocado)
|
||||
```
|
||||
|
||||
### Pela porta do app (usuário operando)
|
||||
|
||||
```
|
||||
MacApp ──Process + argv JSON──► admin/models_api.py
|
||||
│ handlers[comando]
|
||||
▼
|
||||
admin/api/<assunto>.py
|
||||
│ shared.emit() devolve JSON-lines
|
||||
▼
|
||||
fcpxml/ (ou chama um handler do server)
|
||||
▼
|
||||
arquivo gerado + caminho de volta ao app
|
||||
```
|
||||
|
||||
A saída da ponte é **JSON-lines**: um documento JSON por linha, para que
|
||||
comandos longos transmitam progresso enquanto rodam. Toda escrita passa por
|
||||
`admin/api/shared.py::emit`, que serializa o acesso a stdout — dois comandos
|
||||
escrevendo ao mesmo tempo entrelaçariam documentos.
|
||||
|
||||
---
|
||||
|
||||
## 4. Dual-mode: XML + Live
|
||||
|
||||
O sistema opera em **dois modos complementares**:
|
||||
|
||||
- **Modo XML (principal):** exporta FCPXML, processa como dados, reimporta.
|
||||
Roda fora do FCP. Nenhuma API privada.
|
||||
- **Modo Live (`fcpxml/live.py`):** *push* do FCPXML direto p/ o FCP em
|
||||
execução via Apple events oficiais (`Open Document`), com `import-options`.
|
||||
Leitura de bibliotecas via AppleScript read-only.
|
||||
- **Modo Live (`fcpxml/live.py`):** *push* do FCPXML direto para o FCP em
|
||||
execução via Apple events oficiais (`Open Document`). Leitura de bibliotecas
|
||||
via AppleScript read-only.
|
||||
|
||||
**Assimetria estrutural:** import é scriptable, mas a Apple não oferece export
|
||||
programático — round-trips voltam pelas ferramentas XML.
|
||||
programático. Round-trips sempre voltam pelas ferramentas XML.
|
||||
|
||||
---
|
||||
|
||||
## 5. Onde está cada responsabilidade
|
||||
|
||||
| Responsabilidade | Fica em |
|
||||
|------------------|---------|
|
||||
| Modelos de dados (tempo, clips, markers) | `fcpxml/models.py` |
|
||||
| Modelos de dados (tempo, clips, markers, QC, legendas) | `fcpxml/models/` |
|
||||
| Parse FCPXML → objetos | `fcpxml/parser.py` |
|
||||
| Editing/escrita (modifier + writer) | `fcpxml/writer.py` |
|
||||
| Edição e escrita de FCPXML | `fcpxml/writer/` |
|
||||
| Geração de timeline nova | `fcpxml/rough_cut.py` |
|
||||
| Comparação de timelines | `fcpxml/diff.py` |
|
||||
| Export cross-NLE (Resolve, FCP7) | `fcpxml/export.py` |
|
||||
| Inteligência de mídia (silêncio/beats) | `fcpxml/media_intel.py` |
|
||||
| Transcrição Whisper local | `fcpxml/transcribe.py` |
|
||||
| Silêncio e beats | `fcpxml/media_intel.py` |
|
||||
| Transcrição Whisper | `fcpxml/transcribe.py` |
|
||||
| Diarização (quem falou) | `fcpxml/diarize.py` |
|
||||
| Ênfase acústica | `fcpxml/emphasis.py`, `fcpxml/voice_features.py` |
|
||||
| Timeline de voz (o JSON que a IA lê) | `fcpxml/voice_timeline.py` |
|
||||
| Decisões de edição (cut/zoom/text/marker) | `fcpxml/voice_actions.py` |
|
||||
| Revisão de frases da etapa 5 | `fcpxml/phrase_review.py` |
|
||||
| Layout de legendas e métricas de fonte | `fcpxml/text_layout.py`, `font_metrics.py`, `collision.py` |
|
||||
| Gestão de modelos Whisper | `fcpxml/model_manager.py` |
|
||||
| Templates de timeline | `fcpxml/templates.py` |
|
||||
| Controle Live do FCP | `fcpxml/live.py` |
|
||||
| Segurança XML (`defusedxml`, `serialize_xml`) | `fcpxml/safe_xml.py` |
|
||||
| Segurança XML | `fcpxml/safe_xml.py` |
|
||||
| Validação contra DTDs da Apple | `fcpxml/dtd.py` |
|
||||
| Transporte MCP (73 tools) | `server.py` |
|
||||
| Transporte MCP (74 tools) | `server.py` + `server_tools/` |
|
||||
| Ponte com o app (37 comandos) | `admin/models_api.py` + `admin/api/` |
|
||||
| Interface do usuário | `MacApp/Sources/` |
|
||||
|
||||
## 6. Mapa de dependências (você está aqui se for mexer no X → quem tocar)
|
||||
---
|
||||
|
||||
## 6. Mapa de dependências
|
||||
|
||||
```
|
||||
server.py ──► fcpxml/parser, writer, rough_cut, export, diff,
|
||||
media_intel, transcribe, templates, live, dtd
|
||||
admin/models_gui.py ──► fcpxml/media_intel, model_manager,
|
||||
parser, transcribe
|
||||
admin/models_api.py ──► fcpxml/model_manager
|
||||
fcpxml/writer.py ──► fcpxml/models, safe_xml, dtd
|
||||
fcpxml/__init__.py ──► reexporta a API pública
|
||||
MacApp/ ──► admin/models_api.py (subprocesso, por caminho)
|
||||
admin/api/ ──► fcpxml/* e, para algumas operações, server.py
|
||||
server.py ──► server_tools/*
|
||||
server_tools/* ──► server_tools/_shared/ ──► fcpxml/*
|
||||
fcpxml/writer/ ──► fcpxml/models/, safe_xml, dtd, text_layout, collision
|
||||
fcpxml/models/ ──► fcpxml/text_layout (só o pacote subtitles)
|
||||
fcpxml/__init__.py ──► reexporta a API pública
|
||||
```
|
||||
|
||||
> Se você cria uma **nova ferramenta MCP**, o trabalho principal é em `fcpxml/`
|
||||
> (função pura + testes). O handler em `server.py` fica fino: validação de
|
||||
> caminho → `_parse_project` → chama a função → `_text_result`.
|
||||
**A seta que não existe, e não deve existir:** `fcpxml/` nunca importa de
|
||||
`server_tools/`, de `admin/` ou de qualquer coisa que saiba o que é uma tool.
|
||||
Se você precisar disso, a lógica está no lugar errado.
|
||||
|
||||
---
|
||||
|
||||
## 7. Criando algo novo — por onde começar
|
||||
|
||||
| Você quer… | Comece por |
|
||||
|-----------|-----------|
|
||||
| Uma **ferramenta MCP** nova | Função pura em `fcpxml/` + teste. O handler em `server_tools/` fica fino. |
|
||||
| Um **comando do app** novo | Mesmo caminho, e exponha em `admin/api/<assunto>.py` + tabela em `models_api.py`. |
|
||||
| Uma **tela** nova | `MacApp/Sources/`, consumindo comandos que já existem na ponte. |
|
||||
| Uma **regra de edição** nova | `fcpxml/` sempre. Se você está escrevendo `if` sobre timeline fora de `fcpxml/`, pare. |
|
||||
|
||||
O trabalho principal é **sempre** no engine. As camadas de cima são finas de
|
||||
propósito: é o que permite testar 1.454 casos sem abrir o app nem subir o MCP.
|
||||
|
||||
Reference in New Issue
Block a user