From 7921b95a29e381206f645b04ec4e94ec9780045d Mon Sep 17 00:00:00 2001 From: Nilton Constantino Date: Fri, 19 Jun 2026 13:35:31 +0100 Subject: [PATCH] World Editor / Journey Spec --- discussion/index.ndjson | 2 + .../AGD-0001-world-editor-journey-spec.md | 207 ++++++++++++++++++ .../DEC-0001-world-editor-journey-v1.md | 143 ++++++++++++ .../plans/PLN-0001-world-journey-spec.md | 100 +++++++++ .../plans/PLN-0002-journey-runtime-model.md | 110 ++++++++++ .../plans/PLN-0003-world-editor-authoring.md | 114 ++++++++++ .../PLN-0004-journey-commit-validation.md | 118 ++++++++++ 7 files changed, 794 insertions(+) create mode 100644 discussion/index.ndjson create mode 100644 discussion/workflow/agendas/AGD-0001-world-editor-journey-spec.md create mode 100644 discussion/workflow/decisions/DEC-0001-world-editor-journey-v1.md create mode 100644 discussion/workflow/plans/PLN-0001-world-journey-spec.md create mode 100644 discussion/workflow/plans/PLN-0002-journey-runtime-model.md create mode 100644 discussion/workflow/plans/PLN-0003-world-editor-authoring.md create mode 100644 discussion/workflow/plans/PLN-0004-journey-commit-validation.md diff --git a/discussion/index.ndjson b/discussion/index.ndjson new file mode 100644 index 0000000..36948f8 --- /dev/null +++ b/discussion/index.ndjson @@ -0,0 +1,2 @@ +{"type":"meta","next_id":{"DSC":2,"AGD":2,"DEC":2,"PLN":5,"LSN":1,"CLSN":1}} +{"type":"discussion","id":"DSC-0001","status":"accepted","ticket":"world-editor-journey-spec","title":"World Editor / Journey Spec","created_at":"2026-06-19","updated_at":"2026-06-19","tags":["world-editor","journey","stage-contract","architecture"],"agendas":[{"id":"AGD-0001","file":"AGD-0001-world-editor-journey-spec.md","status":"accepted","created_at":"2026-06-19","updated_at":"2026-06-19"}],"decisions":[{"id":"DEC-0001","file":"DEC-0001-world-editor-journey-v1.md","status":"accepted","created_at":"2026-06-19","updated_at":"2026-06-19","ref_agenda":"AGD-0001"}],"plans":[{"id":"PLN-0001","file":"PLN-0001-world-journey-spec.md","status":"review","created_at":"2026-06-19","updated_at":"2026-06-19","ref_decisions":["DEC-0001"]},{"id":"PLN-0002","file":"PLN-0002-journey-runtime-model.md","status":"review","created_at":"2026-06-19","updated_at":"2026-06-19","ref_decisions":["DEC-0001"]},{"id":"PLN-0003","file":"PLN-0003-world-editor-authoring.md","status":"review","created_at":"2026-06-19","updated_at":"2026-06-19","ref_decisions":["DEC-0001"]},{"id":"PLN-0004","file":"PLN-0004-journey-commit-validation.md","status":"review","created_at":"2026-06-19","updated_at":"2026-06-19","ref_decisions":["DEC-0001"]}],"lessons":[]} diff --git a/discussion/workflow/agendas/AGD-0001-world-editor-journey-spec.md b/discussion/workflow/agendas/AGD-0001-world-editor-journey-spec.md new file mode 100644 index 0000000..6a7a7cd --- /dev/null +++ b/discussion/workflow/agendas/AGD-0001-world-editor-journey-spec.md @@ -0,0 +1,207 @@ +--- +id: AGD-0001 +ticket: world-editor-journey-spec +title: World Editor / Journey Spec +status: accepted +created: 2026-06-19 +resolved: 2026-06-19 +decision: DEC-0001 +tags: + - world-editor + - journey + - stage-contract + - architecture +--- + +## Contexto + +O World Editor precisa organizar o macro-mundo jogavel de Gardens of Nil sem assumir responsabilidade de Stage Editor. O modelo proposto separa: + +- World Editor: camadas macro, tiles ocupados por Stage, contratos de conexao, regras de travessia, loading/progressao e vizinhanca. +- Stage Editor: geometria interna, props, inimigos, puzzles, floors internos, camera interna, portas finais e navegacao dentro do Stage. + +A unidade base proposta e `WorldTile = 24m x 24m`, com cada tile representando aproximadamente uma tela jogavel. O mundo e editado como 2D por `WorldLayer`; verticalidade interna continua no Stage Editor, e verticalidade macro fica sob `JEndpointLift`. + +O sistema central de conexao entre Stages se chama `Journey`. Cada `Stage` declara `JEndpoints`, e cada `JConnection` conecta exatamente dois endpoints. Todo espaco fisico pertence sempre a um Stage; se for necessario corredor, antecamara, sala de loading, elevador longo ou mecanismo de transicao, isso deve virar outro Stage, nao uma conexao com area propria. + +Contexto adicional levantado durante a discussao: o Stage Editor ja esta em andamento em `Assets/Scripts/Editor/GardensStageEditor`, com `StageDefinition` em `Assets/Scripts/Editor/GardensStageEditor/StageDefinition.cs`. Esse `StageDefinition` descreve dados editoriais internos do Stage, como floors, grid/cells e objetos de authoring, e por estar sob `Assets/Scripts/Editor` nao deve ser assumido como dado consultavel in game. + +Direcao proposta pelo usuario: `StageDefinition` deve permanecer como dado de editor, enquanto os dados de Journey precisam existir em um pacote runtime consultavel pelo jogo, provavelmente em `Assets/Scripts/Intrepid/Gameplay/Journey`. + +Refinamento proposto pelo usuario: podem existir ambos os ScriptableObjects. Um SO pode ser editorial/editor-only, usado durante authoring de Stage e World. Quando o trabalho for consolidado, o editor gera ou atualiza os dados do lado runtime do Journey. A intencao e evitar que edicao em andamento de stages/world afete playtest ou runtime. + +Refinamento de fluxo editorial: inicialmente existirao dois editores, um World Editor e um Stage Editor. No futuro eles podem ser consolidados em uma unica experiencia, onde um duplo clique em um Stage no World Editor abre o Stage Editor correspondente. Hoje o Stage Editor recebe o `StageDefinition` manualmente; a integracao futura deve remover essa friccao. + +Nomes aceitos para o contrato inicial: `WorldLayoutSO` para o SO editorial do World Editor e `JGraphSO` para o SO runtime consultavel pelo Journey. Elementos do Journey devem usar prefixo `J`; se forem ScriptableObjects, devem tambem terminar com `SO`. + +Escopo aceito para commit/publicacao do World Editor: o botao de commit gera somente os dados logicos do Journey, como graph, endpoints, connections e metadados necessarios para travessia/loading/progressao. Ele nao gera Stages, cenas, geometria interna, objetos internos ou conteudo do Stage Editor. Cada Stage continua sendo gerado/bakeado a partir do Stage Editor. Os dados publicados do Journey servem como contrato de integracao para que os Stages implementem seus endpoints e conexoes correspondentes. + +Fechamentos de V1: `WorldTile = 24m` e `WorldLayerHeight = 24m` devem ser defaults configuraveis no `WorldLayoutSO`, nao constantes hardcoded. `Stage`, `JEndpoint` e `JConnection` devem ter ids estaveis por GUID/string; o sistema de tags existente pode ser util para classificacao/consulta, mas nao substitui identidade estavel. `JEndpointLift` fica limitado a uma unica `JConnection` na V1. `JEndpointAnchor` pode ter multiplas conexoes, mas a selecao de destino ja existe in game e nao precisa ser tratada por esta V1. Loading hints ficam fora da V1; a arquitetura deve apenas permitir adicionar dados depois. O Journey pode ter uma representacao logica de Stage, chamada `JStage`, mas o Stage de cena nao e gerado pelo botao do World Editor. Na V1, a integracao World Editor -> Stage Editor permanece manual. + +Fechamentos finais de V1: `JGraphSO` deve registrar metadata minima de publicacao, incluindo `sourceWorldLayoutId`, `publishedAt`, `schemaVersion` e, se barato, `contentHash`. Requirements ficam como extensao futura de `JConnection`, sem estrutura concreta obrigatoria na V1. Endpoint obrigatorio sem conexao deve bloquear commit como erro; endpoint opcional sem conexao pode ser warning. + +Tecnologia de editor aceita para V1: o World Editor deve usar a mesma abordagem do Stage Editor atual: `EditorWindow` com UI Toolkit (`UnityEngine.UIElements` e `UnityEditor.UIElements`) e canvas custom baseado em `VisualElement.generateVisualContent`. Isso evita introduzir outro stack de window/editor UI e facilita uma futura integracao entre World Editor e Stage Editor. + +## Problema + +Precisamos transformar a proposta de World Editor / Journey em contrato arquitetural consistente antes de editar specs ou codigo. A proposta ja contem muitas decisoes aparentemente firmes, mas ainda ha pontos que mudam serializacao, validacao, fluxo de edicao, runtime e sincronizacao com Stage Editor. + +O risco principal e cristalizar cedo demais um modelo que parece simples no editor, mas que depois nao fecha bem com runtime, loading, authoring de cenas Unity ou validacao entre o contrato runtime do Journey, o `StageDefinition` editor-only e markers reais na Scene. + +## Pontos Criticos + +- `WorldTile` e `WorldLayer` parecem bons como contrato macro, mas precisam ter semantica clara: unidade de authoring, unidade de loading, unidade de progressao, ou apenas visualizacao editorial. +- `Stage` como conjunto de tiles sem overlap simplifica validacao, mas pode restringir intencoes visuais de mundo se designers tentarem modelar fundo, paralaxe ou volumes sobrepostos no World Editor. +- `JConnection` sem espaco fisico e uma regra forte. Ela evita corredores invisiveis no grafo, mas exige disciplina para criar Transition Stages quando a travessia tem presenca fisica. +- `JEndpointSocket`, `JEndpointAnchor` e `JEndpointLift` cobrem tres categorias boas, mas a cardinalidade e compatibilidade precisam virar invariantes do modelo. +- `StageDefinition` existente pertence ao Stage Editor e e editor-only. O Journey precisa de um contrato runtime proprio, separado do asset interno do Stage Editor. +- A fronteira entre dado autoral/editorial e dado runtime precisa ficar explicita: o World Editor pode produzir/validar dados de Journey, mas o jogo nao deve depender de tipos localizados em `Assets/Scripts/Editor`. +- O modelo pode ter dois niveis de SO: um SO editorial para World Editor/authoring e um SO consolidado runtime em Journey. Ainda falta nomear os tipos e definir a direcao de sync/publicacao. +- O fluxo de edicao tambem precisa considerar integracao entre World Editor e Stage Editor: manter editores separados no inicio, mas desenhar o contrato para permitir abrir o Stage Editor a partir do World Editor. +- Requirements podem pertencer a `JConnection` como dados simples ou extensao futura; loading hints ficam fora da V1. +- Stage Editor Sync ainda e direcao futura. Na V1, a integracao entre World Editor e Stage Editor permanece manual. +- O World Editor deve seguir a tecnologia de window ja usada no Stage Editor, hoje localizado em `Assets/Scripts/Editor/GON/StageEditor`. + +## Opcoes + +### Opcao A - Aceitar o modelo proposto como arquitetura base + +- **Abordagem:** tratar o texto atual como base quase fechada para uma decisao normativa, mantendo apenas detalhes menores para planos futuros. +- **Pro:** acelera a criacao de specs e implementacao do editor; reduz ambiguidade no curto prazo. +- **Contra:** pode esconder decisoes ainda nao maduras, especialmente sync com Stage Editor, requirements e lifts multi-parada. +- **Manutenibilidade:** boa se as premissas estiverem corretas; cara se for necessario mudar cardinalidade ou identidade de endpoints depois. + +### Opcao B - Fechar primeiro o contrato minimo do Journey + +- **Abordagem:** separar o nucleo estavel do Journey: `WorldLayer`, `WorldTile`, `Stage`, `JEndpoint`, `JConnection`, compatibilidade, cardinalidade e validacoes de erro. Deixar runtime selection, UX detalhada de wiring, requirements avancados e sync Scene como decisoes subsequentes. +- **Pro:** cria uma base normativa pequena e implementavel sem resolver todo o ecossistema de uma vez. +- **Contra:** exige mais de uma decisao/plano antes de o sistema completo estar especificado. +- **Manutenibilidade:** forte, porque preserva invariantes essenciais e deixa extensoes menos maduras fora do contrato inicial. + +### Opcao B2 - Fechar contrato minimo com separacao editor/runtime + +- **Abordagem:** manter a Opcao B, mas acrescentar como decisao de base que `StageDefinition` existente e editor-only, e que os tipos/dados consultaveis in game do Journey ficam em `Assets/Scripts/Intrepid/Gameplay/Journey`. +- **Pro:** respeita o Stage Editor ja em andamento; evita dependencia runtime em assemblies de editor; cria uma fronteira limpa entre authoring e gameplay. +- **Contra:** exige mapear como o World Editor sincroniza ou exporta dados para o asset runtime do Journey. +- **Manutenibilidade:** mais forte que a Opcao B simples, porque separa explicitamente contratos de authoring e contratos de runtime antes de surgirem referencias cruzadas indevidas. + +### Opcao B3 - Dois SOs com publicacao explicita + +- **Abordagem:** manter um SO editorial para authoring de World/Stage e gerar um SO runtime consolidado para Journey apenas em um passo explicito de publicacao/consolidacao. +- **Pro:** protege playtest de edicoes incompletas; permite validacoes editoriais mais ricas sem carregar complexidade no runtime; reduz acoplamento entre Stage Editor, World Editor e gameplay. +- **Contra:** introduz um pipeline de publicacao que precisa ser validado, versionado e facil de diagnosticar quando editor e runtime divergirem. +- **Manutenibilidade:** forte se o runtime SO for tratado como artefato publicado e verificavel, nao como espelho editado manualmente. + +### Opcao C - Prototipar editor antes de fechar contrato + +- **Abordagem:** implementar um prototipo Unity do World Editor e deixar a experiencia editorial revelar o melhor modelo. +- **Pro:** valida rapidamente fluxo de pintura, conexoes, visualizacao por layer e wiring. +- **Contra:** viola o fluxo do repositorio para mudancas arquiteturais se nao houver decisao e plano antes do codigo; pode gerar codigo descartavel como fonte acidental de verdade. +- **Manutenibilidade:** arriscada se o prototipo virar contrato implicito. + +## Sugestao / Recomendacao + +Recomendacao inicial: seguir a Opcao B3. + +O texto proposto deve virar uma agenda que separa contrato minimo de extensoes. A primeira decisao deveria fechar apenas o nucleo do World Editor / Journey: + +- World Editor e 2D por `WorldLayer`. +- `WorldTile = 24m x 24m`. +- `Stage` pertence a uma unica layer e ocupa um conjunto sem overlap de tiles. +- Todo espaco fisico pertence a `Stage`. +- `JConnection` nunca possui espaco fisico. +- `JEndpointSocket`, `JEndpointAnchor` e `JEndpointLift` sao os tipos iniciais. +- Compatibilidade inicial e apenas tipo igual com tipo igual. +- `JConnection` e sempre uma ligacao 1:1. +- Cardinalidade inicial: socket uma conexao, anchor varias conexoes, lift uma conexao. +- Anchor e Lift nao coexistem no mesmo tile; sockets podem coexistir com ambos. +- Validacoes de erro e warning precisam ser classificadas antes de execucao. +- `StageDefinition` existente em `Assets/Scripts/Editor/GardensStageEditor` permanece editor-only e nao e a API runtime do Journey. +- Os tipos e assets runtime do Journey devem viver fora de `Assets/Scripts/Editor`, com local inicial recomendado em `Assets/Scripts/Intrepid/Gameplay/Journey`. +- O World Editor pode manter um SO editorial proprio e publicar/consolidar um SO runtime do Journey em etapa explicita, para que playtest use apenas dados validados. +- O SO editorial do World Editor deve se chamar `WorldLayoutSO`. +- O SO runtime do Journey deve se chamar `JGraphSO`. +- A publicacao inicial do Journey deve ser uma acao explicita no World Editor, provavelmente um botao de commit/publicacao do Journey. +- O commit do Journey gera somente o contrato logico runtime: graph, endpoints, connections e dados relacionados. Stage generation/bake permanece responsabilidade exclusiva do Stage Editor. +- O Journey pode conter `JStage` como representacao logica runtime/publicada de um Stage no graph. +- O World Editor e o Stage Editor podem nascer separados. Na V1, a integracao entre eles pode continuar manual; a arquitetura deve permitir que futuramente um duplo clique em Stage abra o Stage Editor com o `StageDefinition` correto. +- `WorldTile = 24m` e `WorldLayerHeight = 24m` entram como defaults configuraveis no `WorldLayoutSO`. +- `Stage`, `JStage`, `JEndpoint` e `JConnection` devem ter ids estaveis por GUID/string. Tags podem complementar classificacao e consulta, mas nao substituem ids. +- Loading hints ficam fora da V1; `JConnection` deve continuar extensivel para dados futuros. +- `JGraphSO` deve registrar metadata minima de publicacao: `sourceWorldLayoutId`, `publishedAt`, `schemaVersion` e opcionalmente `contentHash`. +- Requirements ficam fora da estrutura concreta obrigatoria da V1, mas `JConnection` deve permanecer extensivel para recebe-los depois. +- Endpoint obrigatorio sem conexao deve bloquear commit como erro. Endpoint opcional sem conexao pode ser warning. +- A implementacao do World Editor deve usar `EditorWindow` + UI Toolkit, seguindo o padrao do Stage Editor existente. + +As decisoes abertas devem ser registradas como proximos topicos, nao misturadas no primeiro contrato: + +- como o runtime escolhe destino de Anchor multi-conexao; +- representacao visual de conexoes unidirecionais; +- estrutura concreta de requirements; +- protocolo de sync entre Stage Editor markers, `StageDefinition` editor-only e o asset runtime do Journey; +- detalhes de UX de tela propria de wiring; +- loading hints e politicas de streaming. +- consolidacao visual/operacional futura entre World Editor e Stage Editor. + +## Perguntas em Aberto + +- [x] `WorldTile = 24m x 24m` deve ser hardcoded no contrato inicial ou configuravel por `WorldLayoutSO` com default 24m? +- [x] `WorldLayerHeight = 24m` e uma constante normativa, um default editavel, ou apenas uma recomendacao de authoring? +- [x] O `StageDefinition` existente deve ser tratado como editor-only, nao como fonte runtime do Journey? +- [x] O SO editorial do World Editor deve se chamar `WorldLayoutSO`, `WorldAuthoringSO`, ou outro nome? +- [x] O asset runtime consultavel in game deve se chamar `JGraphSO`, seguindo prefixo `J` e sufixo `SO`? +- [x] O World Editor edita diretamente o asset runtime do Journey ou mantem dados editoriais e exporta/sincroniza um asset runtime? +- [x] Qual acao explicita publica/consolida o SO editorial para o SO runtime: botao no editor, menu command, build step, ou combinacao? +- [x] Como o runtime SO registra versao/hash/origem para diagnosticar divergencia entre authoring e playtest? +- [x] O botao de commit do Journey deve apenas validar/publicar `JGraphSO`, ou tambem atualizar/gerar assets auxiliares? +- [x] A integracao por duplo clique deve abrir o Stage Editor existente passando o `StageDefinition` do Stage, ou criar uma camada comum de selecao/editor shell? +- [x] Um `JEndpointLift` deve permanecer limitado a uma unica `JConnection` no contrato inicial? +- [x] Anchor multi-destino precisa de regra runtime agora, ou basta registrar que o endpoint pode ter varias conexoes e adiar a selecao? +- [x] Requirements devem entrar no contrato inicial como estrutura concreta ou apenas como extensao planejada de `JConnection`? +- [x] Loading hints entram na primeira decisao ou ficam para uma decisao de streaming/loading? +- [x] O build valido deve exigir todos os endpoints obrigatorios conectados, ou isso fica como validacao por profile/contexto? +- [x] Como identificar estavelmente endpoints: GUID proprio, id derivado do Stage, ou referencia Unity serializada? +- [x] O Stage Editor Sync e requisito do primeiro plano de execucao ou uma fase posterior? +- [x] Qual tecnologia de window/editor UI deve ser usada pelo World Editor? + +## Criterio para Encerrar + +Esta agenda pode virar decisao quando houver acordo sobre: + +- quais invariantes entram no contrato inicial do World Editor / Journey; +- quais topicos ficam explicitamente fora do primeiro contrato; +- a fronteira de fonte de verdade entre o asset runtime do Journey e os dados editoriais do World/Stage Editor; +- a fronteira entre `StageDefinition` editor-only e o asset runtime consultavel pelo Journey; +- a fronteira entre SO editorial de World e SO runtime consolidado de Journey; +- o mecanismo minimo de publicacao/consolidacao entre authoring e runtime; +- que a publicacao do World Editor e limitada aos dados logicos do Journey; +- os nomes `WorldLayoutSO` e `JGraphSO`; +- o fluxo inicial de integracao entre World Editor e Stage Editor; +- a cardinalidade inicial de Socket, Anchor e Lift; +- a granularidade minima de Requirements e Loading Hints no primeiro spec; +- a estrategia inicial para ids de Stage, endpoint e connection. +- a tecnologia de editor para a primeira implementacao do World Editor. + +## Discussion + +Entrada inicial recebida em 2026-06-19: proposta "Gardens of Nil - World Editor / Journey Spec" com vinte secoes cobrindo objetivo, principios, unidades, Stage, Journey, endpoints, conexoes, validacoes, fluxo de edicao, visualizacao, decisoes confirmadas e questoes abertas. + +Atualizacao de discussao em 2026-06-19: o usuario apontou que o Stage Editor ja esta em andamento em `Assets/Scripts/Editor/GardensStageEditor`, incluindo `StageDefinition`. A orientacao proposta e que `StageDefinition` seja usado somente no editor, enquanto os dados de Journey devem ser consultaveis in game e ficar em `Assets/Scripts/Intrepid/Gameplay/Journey`. Verificacao local confirmou que `StageDefinition.cs` esta sob `Assets/Scripts/Editor/GardensStageEditor` e modela dados internos/editoriais do Stage. + +Atualizacao de discussao em 2026-06-19: o usuario propĂ´s manter ambos os SOs: um SO editorial para edicao de Stage/World e um SO runtime consolidado no lado Journey. A motivacao e evitar que edicoes em andamento afetem playtest. A recomendacao da agenda foi ajustada para dois SOs com publicacao explicita. + +Atualizacao de discussao em 2026-06-19: o usuario definiu os nomes `WorldLayoutSO` para o SO editorial do World Editor e `JourneyGraphSO` para o SO runtime do Journey. Tambem indicou que havera dois editores inicialmente, World e Stage, com possibilidade futura de unificacao; o fluxo desejado e duplo clique em um Stage no World Editor abrir o Stage Editor com o `StageDefinition` correto. Para publicacao, a preferencia inicial e um botao no World Editor para commitar/publicar o Journey. + +Atualizacao de discussao em 2026-06-19: o usuario fechou que o botao de commit do World Editor gera somente as coisas do Journey: graph, connections, endpoints e demais dados logicos. Stages continuam sendo gerados a partir de cada Stage no Stage Editor, pois sao outra preocupacao. Os SOs de endpoints e connections publicados pelo World Editor devem facilitar a integracao posterior com os Stages. + +Atualizacao de discussao em 2026-06-19: o usuario fechou os defaults e escopo de V1. `WorldTile` e `WorldLayerHeight` ficam como defaults configuraveis de 24m no `WorldLayoutSO`. Identidade deve usar GUID/string estavel, com tags como apoio de classificacao quando util. `JEndpointLift` fica 1:1. Anchor multi-destino pode existir no graph, sem tratar selecao nesta V1 porque a selecao ja existe in game. Loading hints ficam fora da V1. O Journey pode ter `JStage` como representacao logica de Stage. O botao do World Editor nao gera Stage de cena; bake continua no Stage Editor. Integracao World/Stage Editor fica manual na V1. + +Atualizacao de discussao em 2026-06-19: o usuario definiu a convencao de nomes do Journey: elementos usam prefixo `J` e ScriptableObjects terminam com `SO`. Com isso, o runtime graph passa a ser `JGraphSO`. Tambem aceitou as sugestoes finais: metadata minima de publicacao no SO runtime, requirements como extensao futura de `JConnection`, e validacao onde endpoint obrigatorio sem conexao e erro enquanto endpoint opcional sem conexao e warning. + +Atualizacao de discussao em 2026-06-19: o usuario definiu que o World Editor deve usar a mesma tecnologia ja usada no Stage Editor para gerenciamento da Window. Verificacao local mostrou que o Stage Editor atual esta em `Assets/Scripts/Editor/GON/StageEditor`, usando `EditorWindow`, UI Toolkit (`UnityEngine.UIElements` / `UnityEditor.UIElements`) e canvas custom via `VisualElement.generateVisualContent`. + +## Resolution + +Aceita em 2026-06-19. + +Resolucao: emitir uma decisao V1 para o World Editor / Journey com `WorldLayoutSO` como SO editorial do World Editor e `JGraphSO` como SO runtime publicado do Journey. O commit do World Editor publica somente dados logicos do Journey: `JStage`, endpoints, connections, graph e metadata de publicacao. Stage scene generation e bake permanecem no Stage Editor. O World Editor V1 deve usar `EditorWindow` + UI Toolkit seguindo o Stage Editor existente. Requirements concretos, loading hints, sync profundo com Stage Editor, wiring avancado e consolidacao visual dos editores ficam fora da V1. diff --git a/discussion/workflow/decisions/DEC-0001-world-editor-journey-v1.md b/discussion/workflow/decisions/DEC-0001-world-editor-journey-v1.md new file mode 100644 index 0000000..0b199a0 --- /dev/null +++ b/discussion/workflow/decisions/DEC-0001-world-editor-journey-v1.md @@ -0,0 +1,143 @@ +--- +id: DEC-0001 +ticket: world-editor-journey-spec +title: World Editor / Journey V1 +status: accepted +created: 2026-06-19 +accepted: 2026-06-19 +agenda: AGD-0001 +plans: + - PLN-0001 + - PLN-0002 + - PLN-0003 + - PLN-0004 +tags: + - world-editor + - journey + - stage-contract + - architecture +--- + +## Status + +Accepted. + +This decision defines the V1 architecture contract for the Gardens of Nil World Editor and Journey runtime graph. + +## Contexto + +Gardens of Nil needs a World Editor to author the macro-world without taking over Stage Editor responsibilities. The Stage Editor already exists under `Assets/Scripts/Editor/GON/StageEditor` and uses `StageDefinition` for editor-only stage authoring. + +The Journey system is the logical contract for travel between Stages. It MUST be available to gameplay/runtime code and MUST NOT depend on types located under `Assets/Scripts/Editor`. + +## Decisao + +The World Editor V1 SHALL use a two-asset model: + +- `WorldLayoutSO` is the editor/authoring ScriptableObject for the World Editor. +- `JGraphSO` is the published runtime ScriptableObject for Journey. + +Journey elements MUST use the `J` prefix. Journey ScriptableObjects MUST also use the `SO` suffix. + +The World Editor SHALL publish Journey data through an explicit commit action in the World Editor UI. This commit MUST generate only logical Journey data: `JGraphSO`, `JStage`, `JEndpoint` records, `JConnection` records, graph topology, validation-relevant flags, and publication metadata. + +The World Editor commit MUST NOT generate Stage scenes, Stage geometry, internal Stage objects, baked Stage content, or Stage Editor data. Stage generation and bake remain Stage Editor responsibilities. + +## Rationale + +Separating `WorldLayoutSO` from `JGraphSO` protects playtest/runtime from incomplete authoring edits. It also keeps editor-only structures out of gameplay assemblies and allows richer authoring data without forcing that complexity into runtime. + +Keeping Stage bake separate preserves the current Stage Editor ownership model. The World Editor defines where Stages are, how they connect, and what Journey contract they expose; the Stage Editor decides how each Stage implements its internal scene. + +Using the same editor window technology as the Stage Editor reduces implementation risk and keeps a future editor consolidation path open. + +## Invariantes / Contrato + +- World Editor is 2D per `WorldLayer`. +- `WorldTile` default size is `24m x 24m`. +- `WorldLayerHeight` default is `24m`. +- `WorldTile` size and `WorldLayerHeight` MUST be configurable on `WorldLayoutSO`; V1 defaults are 24m. +- A Stage belongs to exactly one `WorldLayer`. +- A Stage occupies a set of WorldTiles. +- A WorldTile MUST NOT be occupied by more than one Stage in the same WorldLayer. +- Physical space MUST always belong to a Stage. +- `JConnection` MUST NOT own physical space. +- If a corridor, antecamera, transition room, long lift, or loading mask has physical presence, it MUST be modeled as a Stage. +- `JGraphSO` MAY contain a logical Stage representation named `JStage`. +- `JStage`, `JEndpoint`, and `JConnection` MUST have stable GUID/string ids. +- Tags MAY be used for classification and lookup, but MUST NOT replace stable identity. +- Initial endpoint types are `JEndpointSocket`, `JEndpointAnchor`, and `JEndpointLift`. +- Endpoint compatibility in V1 is same-type only: socket to socket, anchor to anchor, lift to lift. +- `JConnection` connects exactly two endpoints. +- `JEndpointSocket` accepts at most one `JConnection`. +- `JEndpointAnchor` may have multiple `JConnections`. +- `JEndpointLift` accepts exactly one `JConnection` in V1. +- `JEndpointAnchor` and `JEndpointLift` MUST NOT coexist on the same WorldTile. +- Sockets MAY coexist with anchors or lifts. +- Loading hints are out of V1. +- Concrete requirement modeling is out of V1; `JConnection` MUST remain extensible so requirements can be added later. +- A required endpoint without a connection MUST block World Editor commit as an error. +- An optional endpoint without a connection MAY be reported as a warning. +- `JGraphSO` MUST record minimum publication metadata: `sourceWorldLayoutId`, `publishedAt`, and `schemaVersion`. +- `JGraphSO` SHOULD record `contentHash` when cheap and deterministic enough to be useful. + +## Impactos + +Spec: + +- A future spec pass MUST document `WorldLayoutSO`, `JGraphSO`, `JStage`, endpoint types, connection rules, publication metadata, and validation levels in English. + +Runtime: + +- Runtime Journey code SHALL live outside `Assets/Scripts/Editor`, with initial target path `Assets/Scripts/Intrepid/Gameplay/Journey`. +- Runtime MUST consume `JGraphSO`, not `WorldLayoutSO` and not `StageDefinition`. + +Editor: + +- World Editor V1 SHALL use `EditorWindow` with UI Toolkit (`UnityEngine.UIElements` and `UnityEditor.UIElements`). +- World Editor V1 SHOULD use a custom canvas based on `VisualElement.generateVisualContent`, matching the current Stage Editor approach. +- Stage Editor integration remains manual in V1. +- Future integration MAY allow double-clicking a Stage in the World Editor to open the Stage Editor with the correct `StageDefinition`. + +Stage Editor: + +- `StageDefinition` remains editor-only. +- Stage bake/generation remains owned by the Stage Editor. +- Stage scenes are not generated by the World Editor commit button. + +Firmware: + +- No impact. + +Tooling: + +- A plan is required before code/spec execution. +- Validation should distinguish commit-blocking errors from warnings. + +## Fora de Escopo + +- Runtime destination selection UI/logic for multi-connection anchors. +- Visual representation of unidirectional connections. +- Concrete `JRequirement` structure. +- Loading hints and streaming policies. +- Deep Stage Editor marker sync. +- Unified World/Stage Editor shell. +- Automatic Stage Editor opening from the World Editor in V1. + +## Referencias + +- Agenda: `discussion/workflow/agendas/AGD-0001-world-editor-journey-spec.md` +- Existing Stage Editor path: `Assets/Scripts/Editor/GON/StageEditor` +- Existing Stage Definition: `Assets/Scripts/Editor/GON/StageEditor/StageDefinition.cs` + +## Propagacao Necessaria + +- Create a plan from this decision before implementation. +- Add or update English canonical specs for World Editor / Journey before or alongside code execution, depending on the plan. +- Implement runtime Journey types under `Assets/Scripts/Intrepid/Gameplay/Journey`. +- Implement World Editor authoring types under an editor-only path. +- Ensure runtime assemblies do not depend on `Assets/Scripts/Editor` types. + +## Revision Log + +- 2026-06-19: Initial accepted decision from AGD-0001. diff --git a/discussion/workflow/plans/PLN-0001-world-journey-spec.md b/discussion/workflow/plans/PLN-0001-world-journey-spec.md new file mode 100644 index 0000000..63b48aa --- /dev/null +++ b/discussion/workflow/plans/PLN-0001-world-journey-spec.md @@ -0,0 +1,100 @@ +--- +id: PLN-0001 +ticket: world-editor-journey-spec +title: World Editor / Journey V1 Spec +status: review +created: 2026-06-19 +decisions: + - DEC-0001 +tags: + - spec + - world-editor + - journey +--- + +## Briefing + +Write the canonical English specification for the World Editor / Journey V1 contract accepted in DEC-0001. This plan produces documentation only. It does not implement Unity code. + +## Source Decisions + +- `DEC-0001`: World Editor / Journey V1. + +## Target + +Create or update the canonical spec location used by this repository with an English spec covering: + +- `WorldLayoutSO` as editor authoring data. +- `JGraphSO` as runtime Journey data. +- `JStage`, `JEndpoint`, `JEndpointSocket`, `JEndpointAnchor`, `JEndpointLift`, and `JConnection`. +- World tile/layer defaults and configurability. +- Commit/publication boundary. +- Validation error and warning rules. +- V1 exclusions. + +## Scope + +- Inspect the repository for the canonical spec/docs location before editing. +- Add an English World Editor / Journey V1 spec if none exists. +- If a relevant spec already exists, update it instead of creating a parallel source of truth. +- Link the spec back to `DEC-0001`. +- Document that runtime Journey code lives outside `Assets/Scripts/Editor`. +- Document that Stage bake remains owned by the Stage Editor. + +## Out Of Scope + +- C# implementation. +- Unity Editor UI implementation. +- Stage Editor changes. +- Loading hints, concrete requirements, deep Stage marker sync, and editor unification. + +## Execution Plan + +1. Locate canonical docs/specs. + - Use `rg --files` to find existing `spec`, `docs`, or design contract files. + - Target the existing canonical location if present. + - If no canonical spec location exists, create a minimal `specs/` or repository-approved equivalent only after confirming repo convention from nearby files. + +2. Draft the V1 spec. + - Add sections for authoring model, runtime model, endpoint model, connection model, commit flow, validation, and exclusions. + - Use normative language: `MUST`, `MUST NOT`, `SHALL`, `SHOULD`, and `MAY`. + - Keep the spec in English. + +3. Define V1 data contracts in prose. + - `WorldLayoutSO`: default `worldTileSize = 24m`, default `worldLayerHeight = 24m`, stages, occupied tiles, authoring endpoints, and editor metadata. + - `JGraphSO`: `sourceWorldLayoutId`, `publishedAt`, `schemaVersion`, optional `contentHash`, `JStage[]`, `JEndpoint[]`, and `JConnection[]`. + - `JStage`: logical runtime stage record, not scene generation data. + +4. Define validation levels. + - Errors block commit. + - Warnings do not block commit. + - Required endpoint without connection is an error. + - Optional endpoint without connection is a warning. + +5. Document non-goals. + - No loading hints in V1. + - No concrete requirements in V1. + - No Stage scene generation from World Editor commit. + - No automatic World Editor to Stage Editor integration in V1. + +## Acceptance Criteria + +- The spec is in English. +- The spec references `DEC-0001`. +- The spec names `WorldLayoutSO` and `JGraphSO`. +- The spec states that Journey elements use prefix `J`, and Journey ScriptableObjects use suffix `SO`. +- The spec states that World Editor commit publishes only logical Journey data. +- The spec states that runtime MUST consume `JGraphSO`, not `WorldLayoutSO` or `StageDefinition`. + +## Validation + +- Review the spec against every invariant in `DEC-0001`. +- Search the spec for residual Portuguese text before finalizing. +- Confirm no duplicate contradictory spec was created. +- No automated tests are required for this plan. + +## Risks + +- The repository may not yet have a canonical specs directory. +- If spec location is unclear, stop before creating a new documentation tree and ask for direction. +- Over-documenting future features could accidentally make them V1 commitments. diff --git a/discussion/workflow/plans/PLN-0002-journey-runtime-model.md b/discussion/workflow/plans/PLN-0002-journey-runtime-model.md new file mode 100644 index 0000000..2c7be2c --- /dev/null +++ b/discussion/workflow/plans/PLN-0002-journey-runtime-model.md @@ -0,0 +1,110 @@ +--- +id: PLN-0002 +ticket: world-editor-journey-spec +title: Journey Runtime Model +status: review +created: 2026-06-19 +decisions: + - DEC-0001 +tags: + - runtime + - journey + - scriptableobject +--- + +## Briefing + +Implement the runtime Journey data model consumed by gameplay. This plan creates runtime-safe types under `Assets/Scripts/Intrepid/Gameplay/Journey` and keeps them independent from editor-only code. + +## Source Decisions + +- `DEC-0001`: World Editor / Journey V1. + +## Target + +Create the runtime Journey model: + +- `JGraphSO` +- `JStage` +- `JEndpoint` +- `JEndpointSocket` +- `JEndpointAnchor` +- `JEndpointLift` +- `JConnection` +- supporting enums/value types for endpoint kind, directionality, world tile coordinates, socket face, and validation metadata when needed. + +## Scope + +- Runtime serializable data only. +- ScriptableObject asset type for `JGraphSO`. +- Stable GUID/string ids for `JStage`, `JEndpoint`, and `JConnection`. +- Publication metadata on `JGraphSO`. +- Query helpers that do not depend on `UnityEditor`. + +## Out Of Scope + +- World Editor UI. +- Commit/publish pipeline. +- Stage scene generation. +- Stage Editor bake. +- Loading hints. +- Concrete `JRequirement` structure. +- Runtime destination selection UI for multi-anchor connections. + +## Execution Plan + +1. Create runtime folder structure. + - Target `Assets/Scripts/Intrepid/Gameplay/Journey`. + - Preserve existing `.meta` files if Unity already created the folder. + +2. Add `JGraphSO`. + - Implement as a `ScriptableObject`. + - Fields: `sourceWorldLayoutId`, `publishedAt`, `schemaVersion`, optional `contentHash`, `List`, `List`, `List`. + - Add read-only query methods for stages, endpoints, and connections by id. + - Do not reference `UnityEditor`. + +3. Add `JStage`. + - Serializable runtime logical Stage record. + - Fields: stable id, display/name field, world layer, occupied world tiles, optional tag references if compatible with existing tag conventions. + - Do not include Stage scene generation or Stage bake data. + +4. Add endpoint model. + - Use a serializable base record or discriminated model that Unity can serialize reliably. + - Include stable id, owning `JStage` id, world tile coordinate, endpoint kind, required flag, and type-specific data. + - Socket data includes face. + - Anchor data supports multiple connections by model validation, not by embedded scene interaction logic. + - Lift data includes vertical alignment requirements by world tile coordinate and distinct world layer. + +5. Add `JConnection`. + - Fields: stable id, endpoint A id, endpoint B id, directionality. + - Keep extensibility for future requirements without implementing a concrete requirement system in V1. + - Do not add loading hints in V1. + +6. Add query helpers. + - Connections for endpoint id. + - Endpoints for stage id. + - Stage lookup by id. + - Optional lightweight validation-facing helpers without editor dependencies. + +## Acceptance Criteria + +- Runtime Journey files live under `Assets/Scripts/Intrepid/Gameplay/Journey`. +- No runtime Journey file imports `UnityEditor`. +- Journey runtime ScriptableObject is named `JGraphSO`. +- Runtime Journey element names use prefix `J`. +- `JGraphSO` contains publication metadata required by `DEC-0001`. +- `JStage`, `JEndpoint`, and `JConnection` use stable GUID/string ids. +- Loading hints and concrete requirements are not implemented as V1 commitments. + +## Validation + +- Unity compile succeeds. +- Search `Assets/Scripts/Intrepid/Gameplay/Journey` for `UnityEditor`; it must return no runtime dependency. +- Create a temporary `JGraphSO` asset in Unity and verify its serialized fields are visible and persist. +- No automated tests are required for this phase. + +## Risks + +- Unity serialization may not handle polymorphic endpoint classes as desired without `[SerializeReference]`; choose a serialization pattern consistent with the Unity version and repository style. +- Existing tag APIs may be runtime-safe but should not be forced into V1 if they complicate serialization. +- Overbuilding query APIs could harden runtime behavior that is intentionally out of scope. diff --git a/discussion/workflow/plans/PLN-0003-world-editor-authoring.md b/discussion/workflow/plans/PLN-0003-world-editor-authoring.md new file mode 100644 index 0000000..6fc8e91 --- /dev/null +++ b/discussion/workflow/plans/PLN-0003-world-editor-authoring.md @@ -0,0 +1,114 @@ +--- +id: PLN-0003 +ticket: world-editor-journey-spec +title: World Editor Authoring UI and WorldLayoutSO +status: review +created: 2026-06-19 +decisions: + - DEC-0001 +tags: + - editor + - world-editor + - authoring +--- + +## Briefing + +Implement the editor-only World Editor authoring surface and `WorldLayoutSO`. This plan creates the authoring model and window technology, but does not publish `JGraphSO`; publishing is covered by PLN-0004. + +## Source Decisions + +- `DEC-0001`: World Editor / Journey V1. + +## Target + +Create a V1 World Editor under an editor-only path, using the same window technology as the Stage Editor: + +- `EditorWindow` +- UI Toolkit +- custom canvas via `VisualElement.generateVisualContent` + +Create `WorldLayoutSO` as the editor/authoring ScriptableObject. + +## Scope + +- Editor-only `WorldLayoutSO`. +- Editor window shell. +- Top bar with `WorldLayoutSO` selection and create/save controls. +- 2D layer selector. +- Grid canvas for WorldTiles. +- Basic stage footprint authoring. +- Authoring data for sockets, anchors, lifts, and required/optional endpoint flags. +- Manual Stage Editor integration only. + +## Out Of Scope + +- Runtime `JGraphSO` implementation; covered by PLN-0002. +- Commit/publish pipeline; covered by PLN-0004. +- Stage scene bake. +- Automatic double-click opening of Stage Editor in V1. +- Advanced wiring screen. +- Loading hints and concrete requirements. + +## Execution Plan + +1. Create editor folder structure. + - Target `Assets/Scripts/Editor/GON/WorldEditor`. + - Mirror Stage Editor conventions from `Assets/Scripts/Editor/GON/StageEditor`. + +2. Implement `WorldLayoutSO`. + - ScriptableObject under editor-only code. + - Fields: stable layout id, `worldTileSize` default 24, `worldLayerHeight` default 24, stages, authoring endpoints, authoring connections, and editor metadata. + - Stages in `WorldLayoutSO` reference or identify their editor `StageDefinition` where needed, but this remains authoring-only. + +3. Implement authoring records. + - World stage record with stable id, display name, layer, occupied tiles, color, optional `StageDefinition` reference. + - Authoring endpoint records for socket, anchor, and lift. + - Authoring connection records with stable ids and endpoint ids. + - Required endpoint flag. + +4. Implement `WorldEditorWindow`. + - Use `EditorWindow`. + - Use UI Toolkit. + - Add menu item under the same family as Stage Editor, e.g. `Tools/Gardens Of Nil/World Editor`. + - Top bar: `WorldLayoutSO` object field, create button, save button, validate button placeholder, commit button placeholder or disabled until PLN-0004. + +5. Implement `WorldMapCanvas`. + - Use `VisualElement.generateVisualContent`. + - Render current `WorldLayer`. + - Render grid, stage footprints, occupied tiles, endpoints, and basic connection indicators. + - Implement pan/zoom behavior consistent with Stage Editor canvas patterns. + +6. Implement basic editing operations. + - Create/select Stage. + - Paint or remove occupied WorldTiles for selected Stage. + - Prevent obvious same-layer tile overlap at authoring time where possible. + - Add socket on free tile face. + - Add anchor or lift to tile, enforcing no anchor/lift coexistence. + +## Acceptance Criteria + +- World Editor files live under `Assets/Scripts/Editor/GON/WorldEditor`. +- `WorldLayoutSO` defaults to 24m tile size and 24m layer height. +- World Editor opens from Unity menu. +- World Editor uses `EditorWindow` and UI Toolkit. +- Canvas uses `VisualElement.generateVisualContent`. +- `WorldLayoutSO` is not consumed by runtime Journey code. +- Stage Editor integration remains manual in V1. + +## Validation + +- Unity compile succeeds. +- Open the World Editor window in Unity. +- Create a `WorldLayoutSO` asset. +- Author two stages on the same layer and verify same-tile overlap is prevented or reported. +- Add socket, anchor, and lift authoring endpoints. +- Verify anchor and lift cannot coexist on the same WorldTile. +- Verify no generated Stage scene or Stage bake occurs from these authoring actions. +- No automated tests are required for this phase. + +## Risks + +- UI scope can grow quickly; keep V1 focused on authoring enough data for commit. +- If `StageDefinition` moved or changes namespace, keep references isolated inside editor-only code. +- Editor-only `WorldLayoutSO` should not leak into runtime scenes or runtime Journey APIs. diff --git a/discussion/workflow/plans/PLN-0004-journey-commit-validation.md b/discussion/workflow/plans/PLN-0004-journey-commit-validation.md new file mode 100644 index 0000000..d07c22e --- /dev/null +++ b/discussion/workflow/plans/PLN-0004-journey-commit-validation.md @@ -0,0 +1,118 @@ +--- +id: PLN-0004 +ticket: world-editor-journey-spec +title: Journey Commit and Validation +status: review +created: 2026-06-19 +decisions: + - DEC-0001 +tags: + - editor + - validation + - journey +--- + +## Briefing + +Implement the explicit World Editor commit action that validates `WorldLayoutSO` and publishes only logical Journey data into `JGraphSO`. + +## Source Decisions + +- `DEC-0001`: World Editor / Journey V1. + +## Target + +Create the publication pipeline from `WorldLayoutSO` to `JGraphSO`: + +- validation service, +- error/warning reporting, +- commit button behavior, +- deterministic conversion to runtime logical Journey data, +- publication metadata. + +## Scope + +- Editor-only validation and commit code. +- Conversion from World Editor authoring records to runtime `JGraphSO`. +- Generate/update `JStage`, `JEndpoint`, and `JConnection` records. +- Preserve stable ids across repeated commits. +- Block commit on errors. +- Allow commit with warnings. + +## Out Of Scope + +- Stage scene generation. +- Stage bake. +- Loading hints. +- Concrete requirements. +- Runtime traversal behavior. +- Automatic Stage Editor sync. +- Unified editor shell. + +## Execution Plan + +1. Add validation types. + - Target `Assets/Scripts/Editor/GON/WorldEditor`. + - Create severity enum: error and warning. + - Create validation message record with severity, message, and optional authoring object id. + +2. Add `WorldLayoutValidator`. + - Validate same-layer tile overlap. + - Validate connection endpoints exist. + - Validate connection does not connect an endpoint to itself. + - Validate endpoint type compatibility. + - Validate socket cardinality: at most one connection. + - Validate socket face is a free Stage tile face. + - Validate sockets connect opposite faces on adjacent tiles when physically connected. + - Validate anchor/lift cannot coexist on same WorldTile. + - Validate lift alignment by x/y and differing layer. + - Validate lift cardinality: exactly one connection when required, at most one connection in all cases. + - Validate required endpoint without connection as error. + - Validate optional endpoint without connection as warning. + +3. Add `JourneyCommitter`. + - Input: `WorldLayoutSO` and target `JGraphSO`. + - Run validation before writing. + - If errors exist, abort without modifying `JGraphSO`. + - If only warnings exist, allow commit and show warnings. + - Write `sourceWorldLayoutId`, `publishedAt`, `schemaVersion`, and optional deterministic `contentHash`. + - Write `JStage`, endpoint, and connection records only. + - Do not create Stage scenes, Stage geometry, Stage objects, or Stage bake artifacts. + +4. Wire commit into `WorldEditorWindow`. + - Add or enable commit button. + - Let the user choose/create a target `JGraphSO`. + - Display validation messages after commit attempt. + - Mark changed assets dirty and save through Unity asset APIs. + +5. Add validation display. + - Reuse UI Toolkit patterns from the Stage Editor inspector/top bar where practical. + - Show errors and warnings clearly. + - Keep visual polish secondary to correctness for V1. + +## Acceptance Criteria + +- Commit button publishes `JGraphSO` only. +- Commit button never generates Stage scenes or Stage bake output. +- Commit aborts when validation errors exist. +- Commit can proceed with warnings. +- Required endpoint without connection is an error. +- Optional endpoint without connection is a warning. +- `JGraphSO` publication metadata is written on successful commit. +- Repeated commits preserve stable ids rather than regenerating ids from names or positions. + +## Validation + +- Unity compile succeeds. +- Manually verify commit of a minimal valid layout with two stages and a socket connection. +- Manually verify commit aborts when two stages overlap on the same WorldLayer tile. +- Manually verify commit aborts when a required endpoint has no connection. +- Manually verify commit allows an optional unconnected endpoint and reports a warning. +- Manually verify no Stage scene, Stage geometry, or Stage bake output is created by commit. +- No automated tests are required for this phase. + +## Risks + +- Id preservation can be broken if conversion derives ids from mutable fields; use stored stable ids from authoring data. +- Validation and commit can become coupled to UI; keep validation and conversion in separate services. +- Content hash can become noisy if it includes non-deterministic ordering or timestamps; omit or narrow it if deterministic hashing is not cheap.