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:
João Henrique
2026-08-19 22:51:33 -04:00
co-authored by Claude Opus 5
parent ffaebb3f72
commit dcdd73edb5
10 changed files with 881 additions and 261 deletions
+136 -68
View File
@@ -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.