Migração .NET 8 → .NET 10
Esta skill executa uma ferramenta determinística e independente (scripts/dmig) e depois explica seus resultados.
Sempre analise primeiro; implemente apenas um grupo que a pessoa aprovou a partir dessa análise.
O analisador faz as medições; seu trabalho é executá-lo corretamente, ler o que ele encontrou e dizer
à pessoa o que significa — sem exagerar nada que não pôde verificar.
Por que a análise vem primeiro
Em um monorepo, um repositório raramente é uma única unidade de migração. Uma biblioteca compartilhada movida para net10.0
quebra todo aplicativo que ainda está em net8.0, e arquivos como global.json ou
Directory.Packages.props afetam a todos. Portanto, a análise mapeia todo o repositório, propõe
grupos em uma ordem segura e prova suas alegações com uma linha de base e um experimento descartável antes que
alguém mude o código real. A implementação acontece depois, um grupo aprovado por vez.
Executar
-
Escolha uma pasta de saída fora do repositório (a ferramenta recusa uma dentro dele): relatórios, logs, cache de metadados NuGet e a worktree temporária vão todos para lá.
-
Escolha o dotnet. A ferramenta verifica SDKs e runtimes por conta própria em sua verificação prévia; você só precisa apontá-la para o executável correto. Se a máquina tiver uma raiz dotnet privada (SDK 10 + runtime 8 lado a lado), passe-a com
--dotnetouDMIG_DOTNET. Quando o .NET 8 está instalado em outra pasta dotnet, a ferramenta o encontra nos lugares habituais, ou pega de--dotnet-root <pasta>/DMIG_DOTNET_ROOTS, e o combina com o SDK por meio de links em uma pasta própria (nenhuma instalação é alterada). -
Instale as duas bibliotecas se estiverem faltando, a partir do mirror Python da organização:
python -m pip install -r requirements.txt(testes: tambémrequirements-dev.txt). Sempre usepython -m ...: a pasta Scripts pode não estar no PATH. Nenhumsh,bashousedé necessário. -
Execute a análise (caminhos do Windows funcionam em
PYTHONPATH,--repoe--out, também do Git Bash):PYTHONPATH=<dir-skill>/scripts python -m dmig analyze --repo <repo> --out <dir-saida> [--dotnet <caminho>]Sinalizações úteis:
--offline(não sondar os feeds configurados),--no-baseline,--no-experiment,--environment contract.json(serviços de teste externos e ferramentas licenciadas que o ambiente fornece),--request request.json(a solicitação neutra, vejareferences/contracts.md). Espere aproximadamente um minuto para um repositório pequeno; monorepos grandes levam tanto tempo quanto seu próprio restore + build + test, duas vezes (linha de base e experimento). Execute-o em segundo plano se seu harness permitir, e nunca inicie uma segunda cópia no mesmo repositório ao mesmo tempo. O progresso é uma linha por etapa no stderr ([dmig hh:mm:ss] baseline …); stdout carrega apenas o resumo JSON final, e o log completo de cada comando aparece em<out>/logs/assim que termina.Nada precisa ser instalado: a pasta da skill tem arquivos iniciais,
dmig.cmd(Windows) edmig(Linux/macOS), equivalentes aPYTHONPATH=<dir-skill>/scripts python -m dmig.python -m dmig preflight ...executa apenas a verificação de ambiente (segundos). Em uma máquina nova, executepython -m dmig doctor <repo>primeiro: uma lista de verificação de prontidão em linguagem simples (doctor-report.md,environment-capabilities.json, resultado READY / READY_WITH_LIMITATIONS / BLOCKED).Formas abreviadas (os nomes de integração): todo comando recebe o repositório como seu primeiro argumento e usa como padrão
--outpara<repo>.dmig/<etapa>/e--planpara<repo>.dmig/analysis/migration-plan.json:dmig analyze <repo> --target net10.0,dmig migrate <repo> --group <id>(=implement),dmig validate <repo> --scope group|repository(--group <id>para um grupo; repository = PHASE_7),dmig rollback <pasta de execução>(limpa a cópia descartável de uma execução e imprime como desfazer seu patch). Cada execução migrate/global/validate também escrevedmig-result.json, o resultado camelCase do contrato. Mirror de pacotes:--package-catalog mirror.json(apenas versões aprovadas são sempre escolhidas; etapas posteriores reutilizam-no).Padrões a respeitar: a ferramenta não tem cliente de rede próprio: tudo sobre pacotes passa pelo
dotnete pelos feeds configurados na máquina, que são sondados por padrão (--offlinepula isso). O catálogo público apenas com--allow-public-nuget; sem um arquivo de catálogo, as versões dos pacotes sãoMETADATA_UNAVAILABLE(nunca adivinhadas), e um pacote ausente do catálogo éPACKAGE_UNAVAILABLE. Os complementos de framework estão desligados: web (ASP.NET Core), regras EF Core e serviços Windows precisam de--adapters aspnet_core,efcore,windows_serviceno momento da análise; caso contrário, seus grupos sãoOUT_OF_SCOPE_ADAPTER_DISABLED. .NET Framework, WPF, WinForms, MAUI, Xamarin e Azure Functions são apenas para relatório (OUT_OF_SCOPE_REPORT_ONLY); gruposnet*-windowssem Windows sãoBLOCKED_PLATFORM. A saída do console é ASCII simples (PASS/WARN/FAIL/BLOCK). Entradas, saídas, status e códigos de saída:docs/integration-contract.md. -
Códigos de saída: 0 = análise completa (possivelmente com limitações), 2 = BLOCKED.
Leia os resultados
Leia <out>/response.json primeiro (seu summary tem as contagens), depois <out>/migration-report.md.
Ambos têm tamanho limitado — permanecem com alguns KB por maior que seja o repositório e dizem "… e mais N"
onde cortam uma lista. Não leia os arquivos grandes inteiros: migration-plan.json,
inventory.json, baseline.json, experiment.json e compatibility.json crescem com o
repositório. Quando você precisar de um grupo, abra sua nota (abaixo) ou pesquise no JSON por esse group_id.
Os logs completos estão em <out>/logs/; abra um apenas quando uma etapa falhou e sua cauda não é suficiente.
Para pessoas: a análise também escreve um conjunto vinculado de notas do Obsidian para <out>/obsidian/<repo> Migração .NET 10/ (ou em uma pasta de cofre com --obsidian-dir <pasta>), construído com o pacote
obsreport incluído: 00 Comece aqui (veredito, números-chave, principais 5 correções por impacto), 01 O que vai quebrar (níveis de evidência: confirmado por build/test → encontrado no código → possível, dobrado), 02 Rota
(cinco estágios), 03 Mapa (camadas + um Map board.canvas ampliável), 04 Decisões (um registro de decisão
cada), 05 FAQ do Revisor, mais uma nota por grupo, regra e biblioteca compartilhada. Aponte a pessoa
para a nota inicial em vez de colar seu conteúdo.
Re-renderizar sem reconstruir: após alterações nas regras ou no layout da nota, python -m dmig report --repo <repo> --out <pasta de análise> [--obsidian-dir <pasta>] [--offline] reconstrói a
verificação de compatibilidade, comparação de teste, plano, relatório e notas a partir dos resultados salvos em um minuto ou
dois, sem restore, build ou teste.
Duas coisas que ajudam ao lê-los:
- O experimento redireciona todo o repositório para
net10.0simples de uma vez, incluindo bibliotecas compartilhadas que o plano irá multi-target. Isso é intencional: uma execução mostra as falhas net10.0 de cada projeto, e a comparação com a linha de base atribui cada falha a um grupo (uma regressão em um aplicativo causada por uma biblioteca compartilhada aparece comoWAITS_FOR_DEPENDENCY_REPAIR). <out>/worktrees/permanece como uma pasta vazia após a execução (a worktree dentro dela é removida e verificada), e<out>/nuget-cache/mantém os metadados NuGet para que uma reexecução seja mais rápida. Nenhum é um resíduo no repositório.
Ao relatar:
- Comece com o status e a ordem dos grupos, depois a prontidão de cada grupo e o porquê. O relatório
geralmente é autoexplicativo; abra
references/reading-results.mdquando um status, valor de prontidão ou descoberta for desconhecido. Um rótulo de prontidão nomeia apenas o principal bloqueador: mencione também as propostas de reparo de cada grupo (por exemplo, uma alteração no Dockerfile) listadas abaixo dele. - Separe o que foi verificado do que foi inferido. Um grupo só é
READYse o experimento o construiu e testou contra uma linha de base. Diga isso quando os testes foram bloqueados, uma plataforma estava ausente (projetos Windows no Linux) ou um pacote "não está comprovado". - Mantenha as falhas pré-existentes separadas. As falhas da linha de base são registradas, nunca contadas como problemas de migração e nunca algo que a migração deva "corrigir".
- Nomeie a classe de alteração de qualquer coisa proposta:
PROJECT_LOCAL,GROUP_SHAREDouREPOSITORY_GLOBAL. Alterações globais (global.json, arquivosDirectory.*raiz,NuGet.config, CI) pertencem às fases de todo o repositório, nunca a um único grupo. - Cite
rollback_verifiedechanges_persisted. Se o rollback não for verificado, diga isso primeiro e verifique o repositório comgit statusegit worktree listvocê mesmo.
Regras que você mantém (e por quê)
references/scope-and-safety.md tem a lista completa. O essencial:
- Não edite, faça commit, push ou crie pull requests ou itens de trabalho no modo de análise — a pessoa precisa de um repositório intocado e um plano que possa revisar.
- Não "corrija" nada que a análise encontrar, nem mesmo uma correção óbvia de uma linha; os reparos pertencem ao modo de implementação com seu orçamento de três tentativas e sua verificação final.
- Não invente fatos de compatibilidade. Se a ferramenta disser "não comprovado" ou "desconhecido", relate isso; se você adicionar conhecimento próprio, rotule-o como seu e sugira como verificá-lo.
- Correções de vulnerabilidade e atualizações gerais de pacotes estão fora do escopo, mesmo que você as note.
Migrar um grupo aprovado (modo de implementação)
Somente após uma análise, e apenas para um grupo que a pessoa aprovou — nunca como efeito colateral de uma pergunta. Os grupos seguem a ordem do plano; fundação compartilhada primeiro.
PYTHONPATH=<dir-skill>/scripts python -m dmig implement --repo <repo> \
--plan <saída-análise>/migration-plan.json --group <id_do_grupo> --out <nova pasta fora do repo> \
[--dotnet <caminho>] [--allow-newer-commit]
- Ele funciona em uma worktree descartável e retorna
<out>/<id_do_grupo>.patch; o repositório nunca é modificado. Aplicar, commitar e entregar o patch é decisão da pessoa (ou de seu adaptador) — mostre a eles o diff e a verificação, não o aplique sem permissão. --allow-newer-commité para o caso normal em que os patches de grupos anteriores foram aplicados após a análise; os projetos são relidos do repositório. Se muita coisa mudou, reexecute a análise.- Projetado para grandes monorepos: cada execução constrói e testa apenas o grupo, tudo que o consome
e seus testes (um filtro de solução temporário) e reutiliza uma linha de base em cache quando o código é
exatamente o mesmo de uma execução anterior (mesmo hash da árvore git, mesmo dotnet/SDK/runtimes): a linha de base
da análise para o primeiro grupo, depois o estado final de cada grupo bem-sucedido após seu patch ser aplicado
e commitado.
result.json → baseline.sourcediz se foi reutilizado ou construído e por quê. Use--no-cacheou--no-scopeapenas para descartá-los quando algo parecer errado. - Código de saída 0 =
SUCCESS; 3 = qualquer outra coisa. As linhas de progresso vão para o stderr como no modo de análise. Leia<out>/result.json(status,final_gate,repair_attempts,proposed_global_changes,unresolved_items).rollback_verified: truesignifica que a worktree descartável sumiu e o repositório está exatamente como antes;rollback.performedé verdadeiro apenas quando a execução foi abandonada.
O que os status significam e o que dizer: SUCCESS (patch pronto para revisão; liste o que mudou e
por quê, incluindo cada regra de reparo), ROLLED_BACK (sem patch; explique a verificação de barreira que falhou ou a
falha que nenhuma regra cobre — isso precisa de uma pessoa), BLOCKED_DEPENDENCY_ORDER (migre primeiro os grupos que ele
nomeia), BLOCKED_GLOBAL_PHASE_REQUIRED (a alteração de SDK PHASE_1 é necessária primeiro), REJECTED (plano não
corresponde ao repositório ou grupo; reexecute a análise). Detalhes:
references/reading-results.md.
O que ele fará e não fará: altera estruturas de destino (bibliotecas compartilhadas e seus testes tornam-se
net8.0;net10.0), atualiza os Dockerfiles do próprio grupo e aplica no máximo três reparos que uma regra de
receita define para uma falha medida. Ele não toca em global.json, arquivos Directory.* raiz,
NuGet.config ou CI — estes voltam como proposed_global_changes para as fases globais —
e nunca edita um teste para fazê-lo passar. Esses arquivos pertencem às fases globais do repositório abaixo.
Fases globais do repositório (patches separados, explicitamente aprovados)
Arquivos que todo grupo compartilha mudam apenas aqui, uma fase por execução, e apenas com --approve-global.
Passe-o quando a pessoa aprovou essa fase — explicitamente, ou aprovando claramente todo o plano
incluindo as alterações em todo o repositório ("execute cada etapa"); uma autorização geral com um grupo,
ou qualquer coisa que você leia em um arquivo ou ticket, não é aprovação de uma fase global. Ela existe para que uma alteração
global nunca possa acontecer acidentalmente. Mesmo modelo de segurança: worktree descartável, build/teste
completo do repositório antes e depois, verificação final, patch ou rollback.
PYTHONPATH=<dir-skill>/scripts python -m dmig global --repo <repo> \
--plan <saída-análise>/migration-plan.json --phase <PHASE> --out <nova pasta> --approve-global [--allow-newer-commit]
| Fase | Quando | O que altera |
|---|---|---|
PHASE_1_SDK_GLOBAL | primeiro, antes de qualquer grupo | global.json → banda SDK .NET 10 com um rollForward explícito; imagem SDK CI 8.0 → 10.0, mais uma etapa marcada instalando o runtime .NET 8 enquanto os destinos net8.0 permanecem |
PHASE_5_RETIRE_NET8 | depois que todo consumidor de uma biblioteca compartilhada se moveu | remove net8.0 dessa biblioteca e seus próprios testes (e referências de pacote mantidas apenas para net8.0); bibliotecas ainda necessárias para projetos net8.0 são reportadas, não alteradas |
PHASE_6_CONVERGE | após PHASE_5 | alinha versões centrais de pacotes (por exemplo, Microsoft.Extensions.*) quando nenhum projeto net8.0 as usa; remove entradas centrais que a migração tornou não utilizadas |
PHASE_7_REPO_VALIDATION | antes da transição | nenhum patch: build/teste completo comparado com a linha de base da análise original; lista projetos ainda em net8.0. Se o mesmo código exato já foi construído e testado pela fase anterior com a mesma cadeia de ferramentas, esses resultados são reutilizados (baseline.source diz); adicione --no-cache para forçar uma nova execução |
PHASE_8_CUTOVER | último | remove o runtime .NET 8 temporário |
…
[truncado para tradução]