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>
8.7 KiB
01 — Arquitetura do Sistema (G-ART / fcp-mcp-server)
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 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:
┌──────────────────────────┐ ┌──────────────────────────────┐
│ 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. │
└───────────────────────────────────┘
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.
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 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. |
| 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
Pela porta MCP (Claude decidindo a edição)
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
- Modo XML (principal): exporta FCPXML, processa como dados, reimporta.
- 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 sempre voltam pelas ferramentas XML.
5. Onde está cada responsabilidade
| Responsabilidade | Fica em |
|---|---|
| Modelos de dados (tempo, clips, markers, QC, legendas) | fcpxml/models/ |
| Parse FCPXML → objetos | fcpxml/parser.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 |
| 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 |
| Controle Live do FCP | fcpxml/live.py |
| Segurança XML | fcpxml/safe_xml.py |
| Validação contra DTDs da Apple | fcpxml/dtd.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
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
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.