# PR-09 Asset Move Action and Relocate Wizard Domain owner: `docs/studio` ## Briefing Adicionar a action `Move` na área de actions do asset selecionado e conectá-la a um wizard de relocação explícita. O usuário deve escolher o destino do asset e revisar um resumo antes da execução. Depois da confirmação, o Studio não move diretórios localmente. Ele apenas envia um comando de relocação para o packer, que passa a ser o owner completo da mudança: - atualiza o estado interno necessário; - move o diretório do asset dentro da árvore `assets` do projeto; - emite eventos operacionais até a conclusão. Este plano também fecha uma regra operacional que precisa existir nas duas pontas: - o diretório destino final do asset não pode já ser um root de asset; - portanto, o root destino não pode conter `asset.json`. Após a confirmação do resumo, o modal deve entrar em estado de espera com spinner, escutando o evento operacional do packer. Quando a operação terminar: - sucesso: o Studio dispara refresh, fecha o modal e reposiciona a seleção; - falha: o modal sai do estado de espera e mostra a falha sem fechar silenciosamente. ## Objective Entregar um fluxo de `Move` que seja explícito, previsível e compatível com o modelo em que o packer executa a mutação real e o Studio apenas comanda e observa. Após este PR: - a seção `Actions` do selected asset expõe `Move`; - clicar em `Move` abre um wizard dedicado; - o wizard coleta o parent de destino e o nome final do diretório; - o wizard mostra um passo final de resumo antes do comando final; - o Studio não aceita um destino cujo root já contenha `asset.json`; - o packer também rejeita esse destino como regra de segurança e conformance; - a confirmação final envia um comando de relocation para o packer via API; - o modal entra em espera com spinner até receber o evento terminal da operação; - o refresh estrutural só ocorre depois do evento de conclusão do packer. ## Dependencies - [`./PR-05e-assets-staged-mutations-preview-and-apply.md`](./PR-05e-assets-staged-mutations-preview-and-apply.md) - [`./PR-07c-asset-details-and-form-lifecycle.md`](./PR-07c-asset-details-and-form-lifecycle.md) - [`./PR-07d-asset-mutation-and-structural-sync-orchestration.md`](./PR-07d-asset-mutation-and-structural-sync-orchestration.md) - [`../specs/4. Assets Workspace Specification.md`](../specs/4.%20Assets%20Workspace%20Specification.md) - cross-domain reference: [`../../packer/pull-requests/PR-05-sensitive-mutations-preview-apply-and-studio-write-adapter.md`](../../packer/pull-requests/PR-05-sensitive-mutations-preview-apply-and-studio-write-adapter.md) - cross-domain reference: [`../../packer/pull-requests/PR-09-event-lane-progress-and-studio-operational-integration.md`](../../packer/pull-requests/PR-09-event-lane-progress-and-studio-operational-integration.md) ## Scope - adicionar a action `Move` na seção `Actions` do details - introduzir um `Relocate Wizard` efetivamente utilizável pelo selected asset atual - coletar destino por: - parent directory - destination name - target root derivado - mostrar passo final de resumo antes da confirmação - enviar um comando `RELOCATE_ASSET` com `targetRoot` explícito para o packer - abrir estado modal de espera com spinner após confirmação - correlacionar a operação via `operationId` - escutar o evento operacional do packer até `ACTION_APPLIED` ou `ACTION_FAILED` - publicar structural sync explícito apenas após conclusão bem-sucedida - validar no Studio que `targetRoot/asset.json` não exista - validar no packer que `targetRoot/asset.json` não exista, mesmo se o Studio falhar em bloquear antes ## Non-Goals - não redesenhar o mutation preview panel inteiro - não introduzir rename inline fora do wizard - não adicionar batch move - não redefinir semântica de identidade do asset - não permitir fallback para target automático quando o usuário iniciou `Move` - não mover diretórios diretamente pelo Studio fora do comando ao packer ## Execution Method 1. Expor `Move` na seção `Actions`. O botão deve existir apenas quando houver asset selecionado e deve abrir o wizard a partir do `AssetReference` atual. 2. Implementar o wizard de relocação como fluxo explícito de destino. O wizard deve reutilizar a linguagem já existente de `relocateWizard` e coletar: - root atual - diretório parent de destino - nome final do diretório - target root calculado 3. Adicionar validação local de destino. O wizard deve bloquear avanço/confirmação quando: - o target root for igual ao root atual; - o target root sair da área válida esperada; - o target root já contiver `asset.json`. 4. Adicionar passo final de resumo. Antes do comando final, o usuário deve ver um resumo do: - asset atual - root atual - parent de destino - nome final - target root resultante 5. Integrar o wizard ao fluxo de comando assíncrono do packer. A saída do wizard deve virar `PackerMutationRequest(RELOCATE_ASSET, ..., targetRoot)`, sem usar target automático. Depois do `OK`, o Studio inicia a operação, captura o `operationId` retornado e coloca o modal em estado de espera. 6. Esperar conclusão por evento, não por refresh cego. O modal deve escutar `StudioPackerOperationEvent` correlacionado por `operationId`. Regras: - `ACTION_APPLIED`: disparar refresh estrutural, fechar modal e reselecionar o asset relocado; - `ACTION_FAILED`: sair do spinner, manter modal aberto e mostrar a falha; - eventos intermediários como `PROGRESS_UPDATED` podem atualizar a mensagem/estado visual, mas não fecham o modal. 7. Endurecer a regra no packer. O preview/apply de relocation deve recusar destino cujo root já contenha `asset.json`, produzindo blocker claro e estável. 8. Orquestrar o pós-conclusão como mudança estrutural. Relocation bem-sucedida deve acionar sync estrutural explícito e reposicionar a seleção para o asset movido no novo root. ## Acceptance Criteria - `Move` aparece na seção `Actions` do asset selecionado - clicar em `Move` abre wizard dedicado em vez de mutação imediata - o wizard exige destino explícito escolhido pelo usuário - existe passo final de resumo antes da conclusão - `targetRoot/asset.json` bloqueia o fluxo já no Studio - o packer também bloqueia `targetRoot/asset.json` como regra de segurança - o `OK` final envia o comando ao packer e coloca o modal em espera com spinner - o modal só fecha depois de `ACTION_APPLIED` para a mesma operação - falha operacional não fecha o modal silenciosamente - sucesso operacional executa structural sync explícito - a seleção final aponta para o asset já movido, não para o root antigo ## Validation - unit tests para validação de target root no wizard - unit tests para derivação de `targetRoot` a partir de parent + destination name - unit tests para correlação `operationId -> modal state` - unit tests para sucesso/falha dirigidos por `StudioPackerOperationEvent` - unit tests para publication/orchestration de relocation como structural sync - packer tests para blocker quando `targetRoot/asset.json` já existe - smoke test de UI para: - abrir `Move` - editar destino - ver resumo final - bloquear destino inválido - confirmar operação - ver spinner de espera - fechar no evento de sucesso - manter aberto no evento de falha ## Affected Artifacts - `docs/studio/specs/4. Assets Workspace Specification.md` se a regra de destino inválido precisar ser tornada mais explícita - `prometeu-studio/src/main/java/p/studio/workspaces/assets/details/actions/...` - `prometeu-studio/src/main/java/p/studio/workspaces/assets/wizards/...` - `prometeu-studio/src/main/java/p/studio/workspaces/assets/...` mutation orchestration wiring - `prometeu-studio/src/main/java/p/studio/events/...` - `prometeu-studio/src/main/resources/i18n/messages.properties` - `prometeu-packer/src/main/java/p/packer/mutations/...` - testes de Studio para wizard/flow - testes de packer para relocation target validation