migrar-python-rust
Skill14 starsChanged 6 days agoMITView on GitHub
Use quando o pedido é migrar, portar ou "levar para o Rust" uma responsabilidade que hoje mora no backend Python do Hangar (backend/app) para o hangar-server (crates/): rota, ponte, poller, fila, estado ou integração que já existe no Python. NÃO use para recurso novo sem equivalente Python (nasce direto no Rust) nem para correção pontual de bug em código já migrado. Trabalho misto: use só para a parte que existe no Python.
In this repository11 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- rust-code-styleSkill
- rust-crate-architectureSkill
- rust-workspaceSkill
- hangar-novidadeSkill
- hangar-previewSkill
- iphone-wda-sidestoreSkill
- orquestrar-autoSkill
- orquestrarSkill
- provar-telaSkill
- pr-review-autoSkill
The file
Files
Migrar do Python para o Rust
O que o Python aprendeu fica, inclusive o problema que cada mecanismo dele resolve; só o jeito
de fazer muda. Nenhuma linha de Rust antes do inventário e dos contratos aprovados pelo usuário. Cada fase grava um
arquivo em .superpowers/sdd/<data>-<assunto>/: escopo.md, inventario-<area>.md,
decisoes.md, conferencia.md, medicao.md.
Toda diferença entre Python e Rust é classificada pelo usuário (aceitável, bug do Python, fora de escopo), nunca pelo agente: registre e pergunte, uma pergunta por vez, opções letradas.
Nada nasce no Python. O objetivo é tirar o Python do projeto: sem pedido do usuário, no Python
só se apaga código. Toda linha acrescentada lá (lógica, releitura, cache, ponte, rota, repasse,
condição de modo, teste, dublê, fixture) é perguntada antes de escrever, dizendo o que é, por que o Rust sozinho não
basta e o que se perde sem ela. Subagente para e pergunta pela sessão; linha nova no Python sem a
autorização registrada no decisoes.md é defeito na revisão.
1. Delimitar o caminho inteiro → escopo.md
- Escreva a responsabilidade numa frase ("quem responde GET /api/X e lê as fontes Y").
- Liste todo chamador dos dois lados: rotas FastAPI,
/internal/*, pontes*_bridge,to_thread/pool que esperam o Rust, chamadas do Rust ao Python, clientes (packages/core,desktop-native/,mobile/),scripts/, hooks e plugins. Busca sem filtro de pasta; com busca parcial, nunca afirme "ninguém mais chama". Cada chamador vira um item do plano. - Marque toda volta Rust → Python → Rust e Python → Rust → Python: todas somem.
- Estado que o Rust passa a guardar onde o Python relia a fonte viva: todo fim da vida dele entra no escopo e é do Rust — fechar pelo Hangar, processo sair sozinho, morrer por fora, recriar com o mesmo nome, reiniciar o Rust com a vida de pé.
- O caminho Python sai no mesmo trabalho. O repasse ao Rust nas portas 8766 e 8768 também é linha
nova: só com a autorização acima. No modo
pythona rota responde um código de erro, sem reserva. - Divida em rotas e marque de qual rota cada uma depende (lê, chama ou estende o que a outra cria); sem dependência, a rota é independente. Cada rota migra inteira e entra atrás do Supervisor uma por vez, nunca tudo junto.
- Contrato por rota, aprovado antes do Rust: o que se compara (status, corpo JSON, eventos SSE, efeitos em disco, no tmux e em processos) e a lista fechada de diferenças aceitas, cada uma com a regra de comparação e o porquê. O que não está na lista sai igual.
2. Inventário do que o Python aprendeu → inventario-<area>.md
Um subagente só de leitura por arquivo ou área, todos em paralelo. Cada um lista comportamentos observáveis, não funções. Fontes, todas obrigatórias:
- Código: cada ramo,
except, valor padrão, prazo, repetição, cache, trava, ordem de chamadas. - Mecanismo: cada cache, releitura, conferência, trava, espera e ordem vira item com o problema que resolve e o caso que quebra sem ele.
- Comentário de porquê: cada um vira item.
- Testes Python: cada teste é um comportamento; nome e asserções são a especificação.
- Histórico:
git log --followegit blame; cada commitfix(...)vira item com hash, e o diff dele diz o caso exato. - Regras: entradas de
docs/decisoes/*.mde doCLAUDE.mdsobre a área. - Uso real: diário e logs (
log_paths.base(); no Windows%LOCALAPPDATA%\hangar\logs\diario): códigos de erro da área,pool.espera,loop.atraso, rotas com 1 s ou mais. - Plataforma: ramos
os.name, Windows, psmux, encoding, conexão recusada.
Linha: ID | comportamento que precisa ficar | mecanismo do Python | fonte (arquivo:linha, teste,
commit, decisão) | plataforma. Mostre a contagem por área e peça a aprovação do inventário.
3. Requisito, mecanismo e bug → decisoes.md
- Comportamento é requisito: entra no Rust como está. Caso que o Python aprendeu não se reabre.
- Mecanismo do Python (pool/
to_thread, polling comsleep, captura repetida, cache com prazo, subprocesso por chamada, volta Python↔Rust) é redesenhado. Grave o equivalente Rust e o porquê: evento em vez de polling, async sem pool, estado compartilhado, uma fonte por chave. Antes de trocar, odecisoes.mddiz o problema que o mecanismo resolve e como o desenho Rust resolve o mesmo; o caso que quebra sem ele vira teste Rust. Problema não identificado: o mecanismo fica como está ou se pergunta. Só contorno de limite do Python sai sem par. - Bug do Python achado no caminho: registre e pergunte. Enquanto a pergunta está aberta, o padrão provisório é o Rust reproduzir o bug; a correção vem em commit próprio, depois da paridade.
- Diferença aprovada entra no registro (item, antes, depois, motivo, resposta do usuário) e na lista fechada do contrato da rota.
- Sinal do Claude Code ou do Codex vence adivinhação por transcript ou pane: hook, evento do
stream-json, campo da statusline, pedido de controle. Confirme no binário instalado e numa
sessão
cx-*descartável antes de usar.
4. Prender o comportamento antes de portar
- Caracterização: valor esperado só vem de executar o Python, nunca de ler o código. A captura
mora em
.superpowers/sdd/<data>-<assunto>/contract/<area>/, fora do que é commitado, junto com o script do diferencial; grava o commit de origem e o comando que a gerou; nunca é editada à mão. - Diferencial: enquanto o Python existe, as mesmas entradas vão aos dois lados. Cada lado roda
isolado, partindo do mesmo estado inicial restaurado (config dir temporário, sidecars; nunca o
Rust sobre o estado que o Python já alterou), com o mesmo relógio e os mesmos ids injetados (sem ponto de injeção, registre o limite no
conferencia.md). Compara saída e efeitos (arquivos escritos, teclas enviadas ao tmux, processos abertos) pela regra do contrato. - Entradas cruzam cada armadilha abaixo, cada ramo de erro do inventário e cada fim de vida do item 1.4, com a vida nova já de pé no mesmo nome.
- Teste não é ajustado para passar: sem truncar saída, sem comparar mais frouxo que o contrato, sem gravar a saída do Rust como esperada; até o portão 7, Python ausente faz o diferencial falhar dizendo o que não rodou, nunca pular calado.
- Armadilhas de semântica, conferidas em cada item que as toca:
- inteiro sem limite no Python × largura fixa no Rust;
//e%arredondam para baixo (div_euclid/rem_euclid),round()é meio-par;float()aceita"1e3","nan","inf", espaços;json.dumpsescreve1.0eNaN/Infinity, que o serde recusa;- ordem de chave de
dict/JSON (oHashMapnão preserva); re.matché ancorado no início,Regex::is_matchnão;set/frozensetsão iguais sem ordem,Vecnão;lene fatia contam code points, não bytes; surrogates soltos desurrogateescapee de JSON que o serde recusa;None, chave ausente enullsão três casos;"",0e[]são falsos no Python;- entrada que o Python recusava e o Rust passa a aceitar;
- Python relê a fonte a cada chamada × Rust guarda estado: nome não é identidade; o estado leva a da vida (chave) e sai em todo fim de vida;
- resposta vazia com sucesso (
tmux display-messagede pane morto, rc 0) não prova o alvo.
5. Implementar rota a rota
Execução:
- Commite tudo antes de abrir worktrees. Rotas independentes, ou cuja base já foi mesclada,
abrem juntas: um subagente por rota, cada um numa worktree própria a partir da ponta da branch
(no Claude Code,
isolation: "worktree"), no máximo 3 por vez. Todas compilam no target comum da máquina, só o que mudou. Rota que depende de outra espera a base ser mesclada. - O subagente recebe os caminhos absolutos dos arquivos de fase (
.superpowers/não vai para a worktree) e o recorte da rota: as linhas do inventário, o contrato e as decisões que a tocam, as armadilhas da fase 4 e as regras abaixo. Devolve cada ID fechado com o teste que o prova e as linhas RED e GREEN; ID sem teste ou sem RED volta a ele. Só a sessão escreve nos arquivos de fase: a linha nova de inventário (regra 2) e a diferença que pede classificação do usuário vêm no relato, e a sessão grava ou pergunta antes de apagar a worktree. - Rota que toca concorrência, prazo, repetição ou o contrato entre Python e Rust passa, antes da mesclagem, por um revisor de contexto limpo, só leitura, sobre o diff da worktree contra o contrato e o recorte do inventário. A correção volta ao subagente que escreveu; a sessão confere o diff e os testes dela, sem nova rodada do revisor.
- A sessão mescla uma worktree por vez: resolve o conflito, sobe o protocolo (regra 7) e roda os testes da rota e dos arquivos do conflito; falha volta à rota até ficar verde. A bateria inteira roda uma vez, após a última mesclagem, antes do push. Worktree mesclada é apagada; rota que dependia dela abre agora.
Regras de cada item, para o subagente e para a sessão:
- Um teste Rust por item, pela interface pública (rota, ator, fila), com o esperado tirado da captura, do teste Python ou do commit de fix e escrito no próprio teste. RED com falha de asserção, depois GREEN.
- Diferença achada: primeiro um teste que falha por ela, depois o conserto; então varra a classe inteira (os casos irmãos) e registre a linha de inventário que faltava, com o motivo de não ter sido pega (o subagente, no relato).
- Fixtures reais (pane capturado, JSONL real, payload medido), nunca inventadas.
- Toda entrada externa (JSONL, texto de pane, payload HTTP, sidecar) passa por
proptestou fuzz sem pânico. - Falha visível com código; nunca sucesso falso; nunca reserva Python com o Rust de pé.
- Um escritor por worktree. Compile por
scripts/cargo-pinned(target comum, trava,-j 2): pytest e diferencial rodam a cópia que ele fixa nocrates/targetda worktree. Durante a rota, só o teste dela (cargo-pinned test -p hangar-server --test it <filtro>), com porta e config dir próprios da worktree, nunca o backend real. - Contrato interno mudou (rota
/internal, evento, variável do filho): a sessão sobeINTERNAL_PROTOCOL(Rust) eRUST_SERVER_PROTOCOL(Python) no mesmo commit, na mesclagem; o subagente só avisa que a rota dele muda o contrato.
6. Portões de prova
Todos passam antes de dizer "migrado":
- Conferência item a item (
conferencia.md): todo ID de todo inventário do trabalho, de rodadas anteriores também, com situação final:igualcom o teste Rust que reproduz o caso que quebra (linha de código não prova),mudacom a linha do registro ousaicom a prova de que ninguém depende. Dono do mecanismo que saiu do Python não fecha o item: o problema segue com a responsabilidade e é conferido no Rust. Um revisor de contexto limpo confere que cadaigualaponta um teste desse caso. Item sem situação = não pronto. - Relatório do diferencial (
conferencia.md): entradas comparadas, diferenças duras (status, campo, código, efeito) e moles (texto) por tipo. Zero diferenças sobre poucas entradas não fecha. - Revisão de falhas caladas: um revisor independente (
ecc:silent-failure-hunterou equivalente) sobre o diff inteiro; correção volta ao portão 1. - Caminho inteiro:
grepnos dois lados pelos chamadores doescopo.md; nenhum aponta para o Python, nenhuma volta entre os dois. Antes de apagar o Python, cada pytest da área tem par Rust noconferencia.md. - Uso real: release compilado de checkout limpo do commit, com o Python na mesma versão do contrato, no Linux e no Windows (DELPHI-02) quando a área tem ramo de plataforma.
- Desempenho (
medicao.md): o mesmo roteiro antes e depois, p50/p95 das rotas, CPU e memória, também numa máquina Windows fraca como a DELPHI-02 (4 vCPU), e os marcadores do diário (pool.espera,loop.atraso, rotas ≥ 1 s). Procure volta Python → Rust → Python prendendo o pool e fonte apontando para endereço recusado: oGET /api/credenciaisfazia Python → Rust → Python, prendia o pool de 8 threads de uma VM de 4 vCPU e já fez um envio de mensagem esperar 67 s; uma fonte em127.0.0.1recusado custava 2 s por ciclo no Windows. Mais lento sem motivo registrado não fecha. - Limpeza antes do PR: o PR leva o Rust, os testes Rust e a remoção do Python. Saem junto com
o código: os pytests da área, inclusive os que sobem o Rust contra uma referência Python; os
dublês e fixtures que só eles usam; capturas e diferencial; e o teste Rust que só compara com
captura. Teste Rust que fica traz o esperado escrito nele; entrada real (pane, JSONL) fica ao
lado dele. Confira:
git diff --numstat origin/main -- backend/só tem linhas adicionadas com autorização nodecisoes.mdou na constante do protocolo, egit greppelos módulos apagados não acha nada embackend/.
Done when: os arquivos de todas as fases existem, os sete portões passaram, conferencia.md não
tem item aberto, o caminho Python da responsabilidade foi apagado e git worktree list não tem
worktree de rota sobrando.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

