--- description: "Placement exato no Network Editor e workspaces temporários para artistas, com inputs limitados, UI agendada na main thread e restauração compare-and-swap." --- # Workspaces do editor & placement exato A Wave 10 adiciona dois workflows de editor deliberadamente estreitos: placement exato pela tool existente `arrange_network` e um workspace temporário lado a lado com `manage_artist_workspace`. Ambos usam routes estruturadas e autenticadas do bridge e foram desenhados para funcionar com `TDMCP_BRIDGE_ALLOW_EXEC=0`. ::: tip Limite da evidência Os schemas, routes estruturadas, polling do client e contratos de rollback/state-machine da Wave 10 passam no QA offline integrado. O QA live autenticado do build atual também passou apply/replay/undo/redo do placement exato e os ciclos de workspace TOP-restore e PANEL-cancel no TouchDesigner 099 build 2025.32820 com exec do bridge desabilitado. Edge cases e plataformas não testados continuam explicitamente **UNVERIFIED**. Esses limites seguem explícitos no release v0.13.2. ::: Nenhum dos workflows abre UI arbitrária, expõe raw Python, carrega ou encerra um projeto, nem cria uma transação de undo entre requests. Panic, blackout e outros caminhos de emergência nunca chamam nem esperam o ciclo do workspace. ## Placement exato com `arrange_network` `arrange_network` preserva seu layout automático existente e ganha um branch aditivo `layout_mode: "explicit"`. O modo explícito posiciona filhos imediatos existentes de um COMP em coordenadas exatas `nodeX` / `nodeY` do Network Editor. Ele não cria, exclui, renomeia, troca parent, conecta ou desconecta operadores. ```json { "path": "/project1/show", "layout_mode": "explicit", "positions": { "/project1/show/glsl1": [200, -120], "/project1/show/glsl1_pixel": [430, -220] }, "target_source": "provided_paths", "include_docked": true } ``` O contrato explícito é limitado: - `positions` contém de 1 a 256 paths absolutos e normalizados. Cada path deve ser um filho imediato de `path`. - As coordenadas são inteiros seguros entre `-1_000_000` e `1_000_000`. - `target_source: "provided_paths"` independe da UI e nunca infere seleção. - `target_source: "active_selection"` aceita no máximo 64 paths. O pane ativo precisa ser um Network Editor cujo owner é `path`, e seu conjunto exato de selected/current precisa corresponder às chaves de positions. UI ausente ou Perform Mode nunca significa aprovação para mover mais nodes. - O modo explícito exige `recursive: false`, `annotation_aware: false` e `resize_annotations: false`. Ele não roda o planner automático nem o annotation-aware. - Uma chave de idempotência opaca pode ser enviada para recuperação após perda de resposta. Se omitida, o client cria uma para aquela invocação. Reutilizar a mesma chave com outro input ou outro estado live falha fechado. ### Precedência de operadores docked O TouchDesigner 2025.32820 não moveu DATs diretamente docked quando `nodeX` / `nodeY` do host mudou por programa. Por isso o modo explícito resolve docking antes de qualquer escrita: 1. Um filho docked presente em `positions` vai para sua coordenada exata. O filho explícito sempre vence. 2. Com `include_docked: true`, um filho **diretamente** docked e não nomeado acompanha o host pelo mesmo delta. 3. Com `include_docked: false`, só os operadores nomeados explicitamente se movem. 4. Ownership ambíguo, ciclos, chains docked aninhadas não suportadas ou coordenada carregada fora do limite rejeitam o plano inteiro antes da mutação. ```text host: 0, 0 -> 200, -120 (explícito) docked nomeado: 40, -90 -> 430, -220 (explícito vence) docked sem nome: 40,-180 -> 240, -300 (delta do host) ``` O client primeiro lê contexto e fingerprint escalares e compactos, então envia uma única mutação `POST /api/editor/reposition`. O bridge recalcula o contexto, captura snapshot de todas as posições afetadas, aplica e lê de volta todas as coordenadas, e restaura o snapshot completo após falha parcial. Fingerprint stale causa zero escritas. Receipts distinguem `applied`, `unchanged`, `replayed` e `failed`, incluem posições previous/requested/final por path e informam rollback sem esconder falhas. O apply é uma request REST mutante, então o wrapper de undo por request existente pode cobrir o placement completo. Um undo label só aparece quando o stack live prova exatamente um novo item nativo; ainda não existe promessa de um único undo para várias requests REST ou um turno inteiro do agente. ## Workspaces temporários com `manage_artist_workspace` `manage_artist_workspace` gerencia um layout temporário pertencente ao bridge por processo do bridge. Ele reutiliza um Network Editor existente e adiciona exatamente um viewer pane à direita. Nunca cria janela flutuante nem preset de panes persistente. O ciclo tem quatro ações: | Ação | Objetivo | | --- | --- | | `open` | Agenda um split limitado para o próximo frame do TouchDesigner e retorna imediatamente. | | `status` | Lê um receipt compacto pelo `workspace_id` opaco; nunca toca na UI. | | `restore` | Agenda restauração compare-and-swap do layout exato pertencente ao bridge. | | `cancel` | Cancela antes do apply ou executa a mesma restauração verificada se o apply venceu a corrida. | ### Abrir um workspace de output TOP ```json { "action": "open", "network_path": "/project1/show", "viewer_path": "/project1/show/out1", "viewer_mode": "top_output", "split_ratio": 0.62, "lease_seconds": 300 } ``` `top_output` exige que `viewer_path` resolva para um TOP. Um pane `TOPVIEWER` **não** aceita esse TOP diretamente como owner. O bridge precisa: 1. capturar snapshot do COMP pai do TOP e de seu current child anterior; 2. tornar o TOP solicitado o current child do parent; 3. atribuir esse COMP pai como owner do `TOPVIEWER`; e 4. incluir tanto o owner quanto o current-child na restauração compare-and-swap. Se qualquer parte desse estado mudar, a restauração entra em conflito em vez de sobrescrever a edição do artista. ### Abrir um workspace de controles de painel ```json { "action": "open", "network_path": "/project1/show", "viewer_path": "/project1/show/controls", "viewer_mode": "panel_controls" } ``` `panel_controls` exige um COMP capaz de ser panel. Esse próprio COMP é o owner do `PaneType.PANEL`. Tipos arbitrários de pane, direções de split, nomes de pane, geometria de monitor e flags force não são aceitos. Nos dois modos, `network_path` e `viewer_path` precisam ser explícitos, válidos e pertencer à mesma raiz de projeto. `split_ratio` é a parcela do Network Editor existente e fica entre `0.35..0.75` (default `0.62`). `lease_seconds` fica entre `30..900` (default `300`). Só um workspace não terminal é permitido. ### Polling assíncrono e verificação de close As requests iniciais de `open`, `restore` e `cancel` não esperam pelo trabalho de UI do TouchDesigner. O client faz polling a cada 50 ms por no máximo 1,5 segundo. Durante o polling de status, timeout ou perda de conexão dispara cancel best-effort e nunca vira claim de sucesso `active` ou `restored`. Se a resposta inicial de `open`, `restore` ou `cancel` for perdida por conexão ou timeout, o client faz exatamente um POST de recovery com o mesmo body e a mesma chave de idempotência somente de transporte. O bridge devolve o receipt original deduplicado, sem repetir a transição. Erros de domínio, autorização e demais 4xx determinísticos nunca são repetidos. Se a segunda resposta também for perdida, não existe terceiro POST: o caller pode ler `status`, e o lease limitado segue autoritativo. Todo objeto do TouchDesigner é resolvido e usado somente dentro de callback do frame seguinte na main thread. O serviço retém estado JSON simples e identidades escalares, nunca proxies de Pane, OP, Run ou callback. Depois de `changeType()`, o proxy antigo do Pane é descartado porque o build live o invalida. `Pane.close()` também é adiado no build validado: o pane fechado ainda pode aparecer durante o mesmo callback. Restore usa, portanto, dois frames: 1. compara o fingerprint pós-open completo e fecha só o pane exato pertencente ao bridge; 2. no frame seguinte, readquire panes por identidade escalar e verifica que o pane pertencente sumiu e o baseline retornou. Até esse readback posterior passar, o receipt continua `restore_scheduled`, `cancel_scheduled` ou `cleanup_scheduled`. Ele não pode alegar `restored`, `cancelled` pós-apply ou `expired`. O primeiro rerun integrado usou uma janela de três readbacks e falhou/compensou com segurança antes de o viewport do Network Editor estabilizar. Aumentar essa janela para 12 expôs um drift posterior da animação de `home()` do Network Editor: a animação sobrevivia à transação. Como a atribuição do owner do viewer é suficiente, o caminho final do workspace não chama mais `home()`; a proteção limitada de 12 readbacks e dois fingerprints idênticos permanece. Um novo rerun TOP chegou a `active`, restaurou o viewport baseline e o manteve idêntico um segundo depois. Nenhuma falha intermediária virou claim falso de sucesso. ### Segurança compare-and-swap O bridge captura somente o estado de UI necessário para reverter sua própria transação de um split. Antes da limpeza ele verifica o fingerprint completo pós-open, o Network Editor de origem e o viewer pane pertencente ao bridge. Uma mudança do artista em owner, current child, viewport, ratio, nome, tipo ou conjunto de panes resulta em `conflicted` sem mutação de limpeza. Não existe caminho force. Perform Mode, operação headless/sem UI, Network Editor compatível ausente, família de target errada, target em outro projeto, limite de panes, erro de agendamento ou target stale falham fechado. Routes de workspace são somente UI, ficam fora do undo do grafo e sempre informam `undo_label: null`. ### Inspecionar e restaurar ```json { "action": "status", "workspace_id": "" } ``` ```json { "action": "restore", "workspace_id": "" } ``` Os estados possíveis incluem `scheduled`, `active`, `restore_scheduled`, `cancel_scheduled`, `cleanup_scheduled`, `restored`, `cancelled`, `expired`, `suppressed`, `conflicted` e `failed`. Um receipt agendado significa progresso, não prova de que o editor mudou. ## Exemplos de evidência Estes labels distinguem evidência observada de comportamento fail-closed esperado; PASS no build atual não promove edge cases ou plataformas não testados. ### PASS — routes autenticadas no build atual ```json { "status": "PASS", "scope": "TouchDesigner 099 build 2025.32820, macOS, bridge autenticado, ALLOW_EXEC=0", "observed": [ "filho docked explícito venceu o carry do host", "filho diretamente docked sem nome acompanhou o delta do host", "o apply explícito teve replay idempotente e um undo/redo nativo cobriu o placement", "o workspace TOP chegou a active com split 0.62/0.38 e restaurou para um pane", "o workspace PANEL chegou a active e cancel restaurou o baseline", "os dois cleanups provaram closed, restored e baseline_verified com undo_label null", "o viewport baseline TOP final permaneceu idêntico um segundo após restore", "restore no-op terminal não aumentou o mapa de idempotência", "acesso sem auth retornou 401 e split inválido 0.1 retornou 400", "nenhum novo THREAD CONFLICT apareceu nos reruns finais isolados" ] } ``` ### FAIL — estado stale ou modificado pelo artista ```json { "status": "FAIL", "reason": "artist_layout_changed", "result": "conflicted", "mutation_applied": false, "message": "O workspace capturado não corresponde mais; nenhum pane foi fechado ou reescrito." } ``` Placement exato também falha com zero escritas para fingerprint stale, selection incompatível, docking ambíguo ou path/coordenada inválidos. Uma falha parcial de setter só é uma falha limpa quando o receipt prova rollback completo. ### UNVERIFIED — evidência restante de edge cases e plataformas ```json { "status": "UNVERIFIED", "pending": [ "CAS live de placement derivado da seleção e falha induzida de apply/rollback", "conflito live por mudança do artista, timeout, disconnect e lease expiry", "supressão live em Perform Mode e layouts incomuns com vários panes", "Windows, TouchPlayer, panes flutuantes, outros builds TD e runtime headless real" ] } ``` ## Compatibilidade e migração - Omitir `layout_mode` continua equivalente a `layout_mode: "auto"`. Chamadas antigas de `arrange_network`, tanto legacy quanto annotation-aware, preservam inputs, planners e formatos de resposta atuais. - Campos exclusivos do modo explícito são rejeitados em auto, em vez de mudar silenciosamente uma chamada antiga. O modo explícito é um novo caminho estruturado; não cria uma segunda tool de placement nem infla o catálogo. - Só a nova route explícita tem garantia de não usar fallback para raw Python. O branch automático legacy preexistente mantém seus requisitos de runtime. - `manage_artist_workspace` é aditiva. Ela não substitui `focus_network_editor` nem `get_editor_context` e não expõe um gerenciador genérico de panes. - Recarregue ou reinstale o bridge de runtime correspondente antes de usar qualquer contrato da Wave 10. Um bridge sem as routes estruturadas deve falhar; clients não podem cair para `/api/exec`. ## Crítica visual limitada `enhance_build.visualCritique` é um branch opt-in da tool existente. Ele preserva a chamada legacy, recebe um TOP explícito e 1–6 alvos numéricos limitados e usa preview-only por default. Mutação ainda exige o broker TD-native **Apply / Keep**; Apply usa CAS vinculado à proposta, readback exato e restore compensatório vinculado a uma capability. A calibração local exata `qwen3-vl:8b-instruct-q4_K_M` passou preview, Apply/readback e restore no TD 2025.32820 com `TDMCP_BRIDGE_ALLOW_EXEC=0`. Shape inválido do modelo e timeout de aprovação ficaram sem escrita. Outros modelos, builds do TD e TD realmente headless seguem **UNVERIFIED**.