@polymorphism-tech/morph-spec 7.4.0 → 7.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +2 -2
  2. package/bin/morph-spec.js +18 -0
  3. package/claude-plugin.json +1 -1
  4. package/docs/CHEATSHEET.md +1 -1
  5. package/docs/QUICKSTART.md +1 -1
  6. package/framework/CLAUDE.md +9 -4
  7. package/framework/MORPH.md +4 -4
  8. package/framework/commands/create-umbraco-lp.md +35 -0
  9. package/framework/commands/migrate-lp-to-umbraco.md +33 -0
  10. package/framework/commands/morph-archive.md +17 -27
  11. package/framework/hooks/claude-code/core/enforce-phase-writes.js +115 -90
  12. package/framework/hooks/claude-code/core/protect-spec-files.js +32 -30
  13. package/framework/hooks/claude-code/core/state-sync.js +16 -48
  14. package/framework/hooks/shared/state-reader.js +74 -8
  15. package/framework/hooks/shared/state-writer.js +124 -0
  16. package/framework/rules/morph-task-tracking.md +69 -49
  17. package/framework/rules/morph-workflow.md +192 -191
  18. package/framework/skills/core/create-umbraco-lp/SKILL.md +252 -0
  19. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +452 -0
  20. package/framework/skills/core/morph-apply/SKILL.md +15 -15
  21. package/framework/skills/core/morph-brainstorming/SKILL.md +5 -5
  22. package/framework/skills/core/morph-eval/SKILL.md +1 -1
  23. package/framework/skills/core/morph-implement/SKILL.md +5 -4
  24. package/framework/skills/core/morph-plan/SKILL.md +6 -6
  25. package/framework/skills/core/morph-proposal/SKILL.md +8 -7
  26. package/framework/skills/core/morph-review/SKILL.md +3 -3
  27. package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
  28. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  29. package/framework/templates/REGISTRY.json +1 -3
  30. package/framework/templates/context/CONTEXT-FEATURE.md +10 -17
  31. package/framework/templates/context/CONTEXT.md +181 -180
  32. package/framework/templates/docs/onboarding.md +122 -118
  33. package/framework/templates/docs/proposal.md +10 -0
  34. package/framework/templates/docs/spec.md +228 -228
  35. package/framework/templates/meta-prompts/parallel-workers/parallel-worker.md +80 -80
  36. package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +90 -89
  37. package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +2 -1
  38. package/framework/templates/meta-prompts/squad-leaders/squad-leader.md +44 -43
  39. package/framework/templates/meta-prompts/validators/checkpoint-validator.md +107 -107
  40. package/package.json +1 -1
  41. package/src/commands/project/update.js +415 -368
  42. package/src/commands/project/worktree.js +212 -210
  43. package/src/commands/state/advance.js +82 -0
  44. package/src/commands/state/archive.js +111 -0
  45. package/src/commands/validation/validate-feature.js +229 -105
  46. package/src/core/state/state-manager.js +238 -66
  47. package/src/dashboard/scanner/state-scanner.js +35 -8
  48. package/src/lib/generators/context-generator.js +12 -8
  49. package/src/utils/file-copier.js +7 -0
@@ -0,0 +1,452 @@
1
+ ---
2
+ name: migrate-lp-to-umbraco
3
+ description: Migra uma landing page existente (React/Vite, Next.js estático ou HTML) para o template Umbraco-headless MORPH_Umbraco_LP_Template entregando a LP EXATAMENTE igual à origem (mesmo CSS, imagens, fontes e comportamento) + Umbraco por cima — obtenção automática do template, inventário de seções, lift-and-rewire verbatim de cada seção para módulo editável, conexão do formulário ao backend de leads e gate de paridade determinístico. Repo-agnóstica: roda em qualquer lugar, sem template nem origem pré-baixados.
4
+ version: core
5
+ when-to-invoke: ao receber instrução "use /migrate-lp-to-umbraco {projeto}" do usuário, ou quando o usuário descrever necessidade de migrar uma LP para Umbraco
6
+ user-invokable: true
7
+ cliVersion: "7.5.0"
8
+ ---
9
+
10
+ # migrate-lp-to-umbraco
11
+
12
+ ## Objetivo
13
+
14
+ Conduzir a migração de uma landing page de origem arbitrária para o stack Umbraco 17 / .NET 10 + Next.js do template `MORPH_Umbraco_LP_Template`, entregando uma LP **visual e funcionalmente idêntica à origem** (mesmo CSS, imagens, fontes, animações e comportamento) com o Umbraco embrulhando o conteúdo por cima — projeto funcional e idempotentemente seedável onde todo conteúdo editorial (textos, imagens, vídeos) fica editável no backoffice, e as credenciais de integração de leads nunca saem do backoffice.
15
+
16
+ **Princípio central — lift-and-rewire, não rebuild.** A paridade exata é garantida **por construção**: copia-se o markup/CSS/classes/assets de cada seção da origem **verbatim** para o componente do módulo, trocando apenas os literais de conteúdo (texto, `src` de mídia) por leituras do backoffice. O render é idêntico porque o markup é o mesmo; o gate de paridade (passo 6) é uma **confirmação** de que o lift foi limpo, não uma ferramenta para descobrir e remendar drift. Reescrever cada seção do zero ("rebuild-then-compare") nunca converge para "exatamente igual" — não fazer isso.
17
+
18
+ O processo é reproduzível para qualquer LP (React/Vite, Next.js estático ou HTML). A skill é **repo-agnóstica**: não depende de nenhum repo de exemplo nem do template estarem pré-baixados — ela obtém o template sozinha (passo 0).
19
+
20
+ ## Quando invocar
21
+
22
+ - Usuário pede "use /migrate-lp-to-umbraco {projeto}" ou equivalente natural ("migrar LP para Umbraco", "portar site para Umbraco headless", "rebuild no template Umbraco").
23
+ - Projeto de origem já existe (brownfield) — LP em React/Vite/Next.js/HTML.
24
+
25
+ ## Argumentos
26
+
27
+ - `{projeto}` — nome do projeto em kebab-case (ex.: `reserva-das-flores`, `lp-imobiliaria`). Se ausente, perguntar via `AskUserQuestion`.
28
+
29
+ ## Pré-condições
30
+
31
+ - **Acesso ao template** — `git` + (preferencialmente) `gh` autenticado com acesso ao repo **privado** `polymorphism-tech/MORPH_Umbraco_LP_Template`. O template **não** precisa estar pré-baixado: o passo 0 o obtém. (Para clone via `git` direto, é preciso credencial HTTPS ou chave SSH com acesso à org.)
32
+ - **Origem acessível** — em uma das duas formas (a skill suporta ambas):
33
+ - **(a) código-fonte local** da LP (React/Vite, Next.js ou HTML) — caminho de **fidelidade máxima**, pois o CSS, o markup e os assets exatos estão disponíveis para o lift verbatim.
34
+ - **(b) apenas a URL** da LP em produção — fallback: scrapear o HTML renderizado, o CSS computado e baixar todos os assets. O mesmo gate de paridade (passo 6) se aplica.
35
+ - **Docker** disponível para boot local (`docker compose`), ou .NET 10 SDK + Node para boot sem container.
36
+
37
+ ## Passo 0 — Obter o template (sempre)
38
+
39
+ A skill **sempre** garante uma cópia local do template antes de qualquer outro passo. Roda em qualquer diretório — não assume nada pré-baixado.
40
+
41
+ **Cache de referência (read-only).** Manter uma cópia canônica do template num cache estável por máquina, para leitura dos arquivos de referência (sinks, `SeedContentHandler`, `moduleRegistry`, e2e) e como base do scaffold:
42
+
43
+ - bash: `TPL="${MORPH_TEMPLATE_CACHE:-$HOME/.morph/templates}/MORPH_Umbraco_LP_Template"`
44
+ - PowerShell: `$Tpl = "$env:USERPROFILE\.morph\templates\MORPH_Umbraco_LP_Template"`
45
+
46
+ **Rotina check-local → clone → pull:**
47
+
48
+ ```bash
49
+ # Bash — obtém ou atualiza o cache do template
50
+ TPL="${MORPH_TEMPLATE_CACHE:-$HOME/.morph/templates}/MORPH_Umbraco_LP_Template"
51
+ if [ -d "$TPL/.git" ]; then
52
+ git -C "$TPL" pull --ff-only || echo "offline — usando cache existente"
53
+ else
54
+ mkdir -p "$(dirname "$TPL")"
55
+ gh repo clone polymorphism-tech/MORPH_Umbraco_LP_Template "$TPL" \
56
+ || git clone https://github.com/polymorphism-tech/MORPH_Umbraco_LP_Template.git "$TPL" \
57
+ || git clone git@github.com:polymorphism-tech/MORPH_Umbraco_LP_Template.git "$TPL"
58
+ fi
59
+ ```
60
+
61
+ ```powershell
62
+ # PowerShell — obtém ou atualiza o cache do template
63
+ $Tpl = "$env:USERPROFILE\.morph\templates\MORPH_Umbraco_LP_Template"
64
+ if (Test-Path "$Tpl\.git") {
65
+ git -C "$Tpl" pull --ff-only
66
+ } else {
67
+ New-Item -ItemType Directory -Force (Split-Path $Tpl) | Out-Null
68
+ gh repo clone polymorphism-tech/MORPH_Umbraco_LP_Template "$Tpl"
69
+ if (-not (Test-Path "$Tpl\.git")) { git clone https://github.com/polymorphism-tech/MORPH_Umbraco_LP_Template.git "$Tpl" }
70
+ if (-not (Test-Path "$Tpl\.git")) { git clone git@github.com:polymorphism-tech/MORPH_Umbraco_LP_Template.git "$Tpl" }
71
+ }
72
+ ```
73
+
74
+ Notas:
75
+ - Branch padrão do template é **`master`** — **não** fazer `git checkout` de outra branch (o template já está generalizado: doctype raiz `lpHome`, scripts `new-lp`, sinks de lead, e2e).
76
+ - `gh repo clone` é o caminho primário por ser repo **privado** (usa o token já autenticado). HTTPS/SSH são fallback.
77
+ - O cache é **somente leitura**: nunca rodar `new-lp` nem editar dentro dele — o scaffold (passo 3) trabalha sobre uma **cópia fresca**.
78
+
79
+ ## Fluxo
80
+
81
+ ### 1. Analisar a origem — capturar PARA UM LIFT VERBATIM
82
+
83
+ O objetivo desta etapa não é "entender" a LP, é **capturar tudo o que é necessário para reproduzi-la byte-a-byte**. Cada item abaixo alimenta o lift do passo 4.
84
+
85
+ **Inventário de seções** — para cada bloco visual da página, documentar:
86
+ - Nome e ordem na página.
87
+ - **Markup e classes exatos**: a árvore DOM e os nomes de classe de cada seção, como estão (não normalizar nem "limpar").
88
+ - Conteúdo textual: títulos, subtítulos, corpo, CTAs, links.
89
+ - Mídia: imagens, vídeos, posters — registrar o caminho/URL de cada um para baixar abaixo.
90
+ - **Comportamento interativo**: animação de scroll/scroll-expand, carrossel, marquee, autoplay de vídeo, hovers, mapa, máscara/validação de formulário — documentar cada um com detalhe suficiente para reproduzir idêntico.
91
+ - Dados estruturados aninhados (ex.: lista de produtos com nome+imagem+descrição, lista de depoimentos).
92
+ - **Breakpoints responsivos** e o que muda em cada um (mobile/tablet/desktop).
93
+
94
+ **Tema e CSS — copiar VERBATIM, não reinterpretar:**
95
+ - Capturar o CSS da origem como está: bloco `@theme`/tokens (variáveis `--color-*`, `--font-*`), classes custom, `@layer`, `@keyframes`, media queries. Os valores serão **portados sem alteração** no passo 4 — não arredondar cores nem aproximar espaçamentos.
96
+ - **Config do Tailwind**: portar a config/tema da origem. Tailwind v4 é CSS-based (`@theme` em `globals.css`). ⚠️ A **mesma classe utilitária renderiza diferente** se a versão ou o tema do Tailwind divergir — portar o tema da origem, **nunca** assumir o do template.
97
+
98
+ **Assets — baixar byte-a-byte e registrar checksum:**
99
+ - Baixar/copiar **todos** os assets: imagens, vídeos, posters, ícones e **fontes** (arquivos `.woff2`/`.ttf` + famílias e pesos exatos). Web-fonts são parte da fidelidade — mesma família, mesmo peso, mesmo arquivo.
100
+ - Registrar o `sha256` de cada asset (vira a base da paridade de checksum no passo 6).
101
+ - **Não reencodar nem otimizar** nenhum asset — qualquer reencode quebra a paridade.
102
+
103
+ **Formulário de captação** — campos presentes, validação no frontend, endpoint atual, integração de CRM/leads.
104
+
105
+ **Origem apenas por URL (fallback):** se não houver código-fonte, scrapear o HTML renderizado, extrair o CSS computado dos elementos e baixar todos os assets referenciados (incluindo fontes). O lift parte do markup renderizado + CSS computado.
106
+
107
+ **Exemplo ilustrativo (LP fictícia):**
108
+ - Origem: `src/App.tsx` (React/Vite) — componentes `Navbar`, `Hero`, `HorizontalScroll`, `Manifesto`, `Galeria`, `Testimonial`, `CTA`, `Footer`.
109
+ - Tema extraído do bloco `@theme` em `src/index.css`: `--color-surface`, `--color-primary`, `--color-tertiary`, etc.; tipografia (ex.) Cormorant Garamond + Jost (baixar os arquivos de fonte).
110
+ - Formulário: campos nome, e-mail, telefone + POST para servidor com integração de leads.
111
+
112
+ Produzir `docs/origem-inventario.md` no repositório destino com esta análise (incluindo a tabela de assets com `sha256`).
113
+
114
+ ### 2. Mapear origem para doctypes/módulos do template
115
+
116
+ Comparar cada seção inventariada com os módulos disponíveis no template:
117
+
118
+ | Módulo (alias uSync) | Propósito |
119
+ |---|---|
120
+ | `heroSection` | Hero genérico com título, subtítulo, imagem/vídeo de fundo e CTA |
121
+ | `diferencialSection` / `diferencialItem` | Lista de diferenciais/features com ícone e descrição |
122
+ | `testimonialSection` / `testimonialItem` | Depoimentos com nome, cargo e texto |
123
+ | `galeriaSection` | Galeria de imagens |
124
+ | `videoSection` | Bloco com vídeo embutido |
125
+ | `equipeSection` / `equipeMembro` | Equipe com foto, nome e cargo |
126
+ | `certificacoesSection` / `certificacaoItem` | Selos e certificações |
127
+ | `localizacaoSection` | Mapa ou endereço |
128
+ | `contatoSection` | Seção de formulário de captação |
129
+ | `customHtmlSection` | Bloco HTML livre (fallback para conteúdo sem módulo dedicado) |
130
+
131
+ Para cada seção da origem, determinar (na ótica do lift verbatim — fidelidade primeiro):
132
+ 1. **Reuso direto de módulo do template**: **somente** quando o markup e o CSS renderizados do módulo do template já são idênticos aos da origem. Se houver qualquer diferença visual, **não** reusar — isso introduz drift. Na dúvida, prefira lift verbatim.
133
+ 2. **Módulo do template com ajuste de campos**: o módulo serve mas precisa de propriedades extras — adicionar no uSync `.config` do módulo e no componente frontend. O markup permanece o da origem.
134
+ 3. **Módulo novo via lift verbatim (caso padrão)**: a seção vira um novo doctype + componente que **reproduz o markup/CSS/classes da origem verbatim** (ver passo 4). Criar o doctype + componente + registro no `moduleRegistry.ts`. Nomear o alias em camelCase com sufixo `Section` (ex.: `manifestoSection`, `galeriaSection`).
135
+
136
+ > Regra prática: a decisão default é **lift verbatim** (opção 3). Reusar um módulo do template (opções 1/2) só quando a paridade visual é garantida — caso contrário a LP migrada deixa de ser "exatamente igual".
137
+
138
+ Documentar decisões e justificativas em `docs/mapeamento-modulos.md`.
139
+
140
+ **Exemplo ilustrativo — módulos lifted da origem:**
141
+ - `heroSection` (lift): Hero com efeito scroll-expand + vídeo de fundo expansível — markup e animação copiados verbatim da origem.
142
+ - `brandStorySection`: Bloco editorial de história da marca com imagem e texto em colunas.
143
+ - `galeriaSection` / `galeriaItem`: Grade de itens com imagem, nome e descrição.
144
+ - `processoSection` / `processoStep`: Processo em etapas (passo a passo numerado).
145
+ - `marqueeSection`: Faixa de texto em loop (palavras-chave da marca).
146
+ - `statsSection`: Contador de métricas (anos, projetos, etc.).
147
+ - Módulos reutilizados diretamente (só porque o render bate com a origem): `testimonialSection`, `contatoSection`.
148
+
149
+ ### 3. Scaffold via new-lp
150
+
151
+ Criar o projeto destino a partir de uma **cópia fresca** do template (nunca dentro do cache do passo 0) e rodar o bootstrap nessa cópia:
152
+
153
+ ```bash
154
+ # Bash (Linux/macOS/WSL) — cópia fresca do cache para o diretório do projeto
155
+ TPL="${MORPH_TEMPLATE_CACHE:-$HOME/.morph/templates}/MORPH_Umbraco_LP_Template"
156
+ cp -r "$TPL" ./{projeto} && cd ./{projeto}
157
+ rm -rf .git && git init # desacopla do histórico do template
158
+ # --lead-integration: none | ghl | prospectpro | webhook
159
+ # --reset-db: primeiro boot limpo de um scaffold novo
160
+ ./scripts/new-lp.sh "Nome do Projeto" admin@projeto.com \
161
+ --lead-integration ghl \
162
+ --reset-db
163
+ ```
164
+
165
+ ```powershell
166
+ # PowerShell (Windows) — cópia fresca do cache para o diretório do projeto
167
+ $Tpl = "$env:USERPROFILE\.morph\templates\MORPH_Umbraco_LP_Template"
168
+ Copy-Item $Tpl ".\{projeto}" -Recurse; Set-Location ".\{projeto}"
169
+ Remove-Item .git -Recurse -Force; git init # desacopla do histórico do template
170
+ # new-lp.ps1 usa parâmetros NOMEADOS (não posicionais/--flag):
171
+ .\scripts\new-lp.ps1 -ProjectName "Nome do Projeto" -AdminEmail admin@projeto.com `
172
+ -LeadIntegration ghl -ResetDb
173
+ ```
174
+
175
+ > Alternativa: em vez de copiar do cache, fazer um clone fresco direto no diretório do projeto (`gh repo clone polymorphism-tech/MORPH_Umbraco_LP_Template {projeto}`) e então `rm -rf .git && git init`.
176
+
177
+ O script:
178
+ - Gera `.env` com `UMBRACO_ADMIN_EMAIL`, `UMBRACO_ADMIN_PASSWORD` (senha aleatória), `UMBRACO_GLOBAL_ID`, `REVALIDATE_SECRET`, `PROJECT_NAME` e `LEAD_INTEGRATION`.
179
+ - Integração de lead selecionada em `--lead-integration` (`none`/`ghl`/`prospectpro`/`webhook`). Em modo não-interativo sem a flag, assume `none`.
180
+
181
+ Nunca commitar `.env`. O cache do passo 0 permanece intacto (read-only) — todo o trabalho acontece nesta cópia.
182
+
183
+ **Smoke-boot antes de qualquer customização** — boot limpo do template para confirmar que o ambiente funciona:
184
+
185
+ ```bash
186
+ docker compose up --build
187
+ ```
188
+
189
+ Backend sobe em `http://localhost:5001`, frontend em `http://localhost:3000`. Acessar `http://localhost:3000` e confirmar que a página carrega sem erro 500. Isso elimina problemas de ambiente antes de introduzir customizações — falha precoce = horas economizadas.
190
+
191
+ **Atenção (gotcha Build do Next.js):** o Dockerfile do frontend executa `next build` na imagem — nesse momento o backend precisa estar acessível para o Next.js buscar conteúdo via Delivery API. Se o build rodar antes do backend estar pronto, o build falha com erro de fetch. O `docker compose` com `depends_on: backend` mitiga isso, mas em CI adicionar um `healthcheck` no serviço `backend` e `condition: service_healthy` no `depends_on` do `frontend`.
192
+
193
+ ### 4. Lift verbatim — portar markup, mídia e tema
194
+
195
+ **Esta é a etapa que garante a paridade exata.** Para cada seção, o trabalho é um **lift-and-rewire**, não um rebuild:
196
+
197
+ 1. **Copiar o componente da origem verbatim** para `frontend/src/components/modules/NomeSection.tsx` — mesmo JSX/HTML, mesmas classes, mesmo CSS, mesma estrutura. Não "limpar", não refatorar, não trocar libs de animação.
198
+ 2. **Trocar apenas os literais de conteúdo** (texto, `src` de imagem/vídeo, labels) por leituras do `data` prop do Umbraco. Tudo o que é layout/estilo/markup permanece igual.
199
+ 3. **Seedar** esses valores via `SeedContentHandler` para baterem com a origem (passo 4b) → o primeiro render é idêntico **por construção**; a editabilidade no backoffice fica por cima sem mudar o markup renderizado.
200
+ 4. **Copiar os assets sem reencode** (passo 4b) e **portar o CSS/tema verbatim** (passo 4c).
201
+
202
+ Os passos 4a-4d abaixo detalham cada parte. A regra que atravessa todos: **se mudou o markup ou o CSS renderizado, não é mais "exatamente igual"**.
203
+
204
+ #### 4a. Módulos novos — uSync ContentType
205
+
206
+ Para cada módulo novo identificado no passo 2, criar o arquivo `.config` em `backend/uSync/v17/ContentTypes/`. Espelhar a estrutura dos módulos existentes (ex.: `heroSection.config`):
207
+
208
+ ```xml
209
+ <!-- Exemplo: atelierSection.config -->
210
+ <?xml version="1.0" encoding="utf-8"?>
211
+ <ContentType Key="{novo-guid}" Alias="atelierSection" Level="2">
212
+ <Info>
213
+ <Name>Atelier Section</Name>
214
+ <Icon>icon-layers</Icon>
215
+ <Thumbnail>folder.png</Thumbnail>
216
+ <Description></Description>
217
+ <AllowAtRoot>False</AllowAtRoot>
218
+ <IsListView>False</IsListView>
219
+ <Variations>Nothing</Variations>
220
+ <IsElement>true</IsElement>
221
+ <Folder>Sections</Folder>
222
+ <Compositions />
223
+ <DefaultTemplate></DefaultTemplate>
224
+ <AllowedTemplates />
225
+ </Info>
226
+ <Structure />
227
+ <GenericProperties>
228
+ <GenericProperty>
229
+ <Key>{guid-prop}</Key>
230
+ <Name>Titulo</Name>
231
+ <Alias>titulo</Alias>
232
+ <Definition>0cc0eba1-9960-42c9-bf9b-60e150b429ae</Definition>
233
+ <Type>Umbraco.TextBox</Type>
234
+ <Mandatory>true</Mandatory>
235
+ <Description><![CDATA[]]></Description>
236
+ <SortOrder>0</SortOrder>
237
+ <Tab Alias="content/content">Content</Tab>
238
+ <Variations>Nothing</Variations>
239
+ </GenericProperty>
240
+ <!-- propriedades adicionais -->
241
+ </GenericProperties>
242
+ </ContentType>
243
+ ```
244
+
245
+ Cada módulo carrega suas próprias propriedades diretamente — os módulos do template não usam compositions compartilhadas para campos de seção (o `<Compositions />` é vazio nos módulos existentes; as compositions em `lpHome.config` são para SEO, cookie e analytics — composições transversais da home, não dos módulos filhos).
246
+
247
+ Para que o novo módulo apareça como opção de bloco na home, registrá-lo no DataType de Block List (`modules` em `lpHome.config`). O `<Definition>649d20e8-e4d5-42a0-9f27-28a537f9ebda</Definition>` aponta para esse DataType — adicionar o novo `ContentTypeKey` no XML do DataType em `backend/uSync/v17/DataTypes/` (arquivo correspondente) e no `SeedContentHandler` para o bloco ser incluído no seed programático.
248
+
249
+ #### 4b. Seed de conteúdo — SeedContentHandler
250
+
251
+ O `SeedContentHandler` (`backend/Infrastructure/SeedContentHandler.cs`) roda na notificação `uSyncImportCompletedNotification` — após o Umbraco importar todos os schemas — e cria a home com conteúdo inicial se ainda não existir. É **idempotente**: verifica se já há um nó raiz do tipo `lpHome` (ou alias equivalente) antes de criar.
252
+
253
+ Padrão de seed de uma seção com mídia:
254
+
255
+ ```csharp
256
+ // 1. Importar asset para a Media Library (idempotente por nome de arquivo)
257
+ var mediaItem = CreateOrGetMedia("hero-video.mp4", mediaFolder, "umbracoMediaVideo");
258
+ // mediaItem.Key é um Guid estável entre boots
259
+
260
+ // 2. Serializar referência como MediaPicker3 JSON
261
+ var mediaJson = JsonSerializer.Serialize(new[] {
262
+ new { key = Guid.NewGuid(), mediaKey = mediaItem.Key }
263
+ });
264
+
265
+ // 3. Setar na propriedade do nó de conteúdo
266
+ sectionNode.SetValue("videoFundo", mediaJson);
267
+ ```
268
+
269
+ Coloque os assets de seed em `backend/seed-assets/` (imagens, vídeos, ícones da LP de origem).
270
+
271
+ **O que vai no SeedContentHandler:** textos de inauguração, mídia local (arquivos em `seed-assets/`), ordem das seções, configuração inicial. O conteúdo fica editável no backoffice após o primeiro boot — o seed é apenas o estado inicial.
272
+
273
+ **O que NÃO vai no SeedContentHandler:** tokens de integração (GHL API Key, webhook URL). Esses ficam no nó `leadsConfig` que o operador preenche diretamente no backoffice após o boot (ver passo 5).
274
+
275
+ #### 4c. Tema — portabilidade do index.css
276
+
277
+ Copiar as variáveis de design da origem para `frontend/src/app/globals.css` no bloco `@theme`:
278
+
279
+ ```css
280
+ /* Antes (origem — index.css/CSS da LP) */
281
+ @theme {
282
+ --color-surface: #F5F0EB;
283
+ --color-primary: #8B1A1A;
284
+ --color-primary-hover: #A82020;
285
+ --color-tertiary: #1A1A1A;
286
+ --font-display: 'Cormorant Garamond', serif;
287
+ --font-body: 'Jost', sans-serif;
288
+ }
289
+
290
+ /* Depois (destino — frontend/src/app/globals.css) */
291
+ @theme {
292
+ --color-surface: #F5F0EB; /* portado diretamente */
293
+ --color-primary: #8B1A1A;
294
+ --color-primary-hover: #A82020;
295
+ --color-tertiary: #1A1A1A;
296
+ --font-display: 'Cormorant Garamond', serif;
297
+ --font-body: 'Jost', sans-serif;
298
+ }
299
+ ```
300
+
301
+ Atualizar os `<link>` de Google Fonts em `frontend/src/app/layout.tsx` para carregar as fontes corretas.
302
+
303
+ #### 4d. Componentes frontend dos módulos novos
304
+
305
+ Para cada módulo novo, criar o componente em `frontend/src/components/modules/NomeSection.tsx` **a partir do componente da origem copiado verbatim** — colar o JSX/markup/classes/CSS da origem e trocar só os literais de conteúdo por `data.*`:
306
+
307
+ ```tsx
308
+ // Padrão: componente com 'use client', recebe { data: SectionProps },
309
+ // faz cast para o tipo tipado, e lê props via data.property.
310
+ // O corpo do return é o MARKUP DA ORIGEM VERBATIM — mesmas tags, mesmas classes,
311
+ // mesma animação. Só os literais (texto, src) viram leituras de `d`.
312
+ 'use client'
313
+ import type { SectionProps, NomeSectionProps } from '@/lib/types'
314
+
315
+ type Props = { data: SectionProps }
316
+
317
+ export default function NomeSection({ data }: Props) {
318
+ const d = data as NomeSectionProps
319
+ // return ( <markup da origem, idêntico, com {d.titulo} no lugar do texto fixo> )
320
+ }
321
+ ```
322
+
323
+ Declarar o tipo correspondente em `frontend/src/lib/types.ts` com os aliases de propriedade do uSync.
324
+
325
+ Registrar o módulo em `frontend/src/components/modules/moduleRegistry.ts`:
326
+
327
+ ```ts
328
+ nomeSection: () => import('./NomeSection'),
329
+ ```
330
+
331
+ ### 5. Conectar o formulário ao backend de leads
332
+
333
+ O template usa uma arquitetura de **multi-sink**: o backend seleciona automaticamente a integração correta pelo valor de `leadIntegration` configurado no nó `leadsConfig` do backoffice.
334
+
335
+ **Sinks disponíveis no template:**
336
+
337
+ | `Key` do sink | Classe | Integração |
338
+ |---|---|---|
339
+ | `ghl` | `GhlLeadSink` | GoHighLevel CRM |
340
+ | `prospectpro` | `ProspectProLeadSink` | ProspectPRO |
341
+ | `webhook` | `WebhookLeadSink` | Webhook HTTP genérico |
342
+
343
+ O `LeadController` (`backend/Controllers/LeadController.cs`) recebe `IEnumerable<ILeadSink>` via DI e despacha para `sinks.FirstOrDefault(s => s.Key == cfg.Integration)`. Adicionar uma nova integração = implementar `ILeadSink` + uma linha no `LeadComposer.cs`.
344
+
345
+ **Configuração pós-boot (nunca em código):**
346
+
347
+ 1. Acessar o backoffice em `http://localhost:5001/umbraco`.
348
+ 2. Navegar até o nó **Leads Config** (criado automaticamente pelo `SeedContentHandler`).
349
+ 3. Preencher:
350
+ - `Lead Integration`: selecionar `ghl` / `prospectpro` / `webhook`.
351
+ - `API Key` / `Webhook URL` / campos específicos da integração.
352
+ 4. Publicar o nó.
353
+
354
+ O token nunca aparece no código-fonte nem em variáveis de ambiente — lido do cache publicado pelo `ReadConfig()` do controller via `IUmbracoContextFactory`.
355
+
356
+ **Frontend:** o componente `ContactSection` (`frontend/src/components/modules/ContactSection.tsx`) faz POST para `/api/leads` (rota do Next.js que faz thin-proxy para `{NEXT_PUBLIC_UMBRACO_URL}/api/leads`). Nenhuma credencial no frontend.
357
+
358
+ **Campos mínimos esperados pelo LeadController:** `name`, `email`, `phone`. Campos extras são aceitos no payload como propriedades livres e repassados ao sink.
359
+
360
+ **Normalização de telefone:** aplicar máscara no frontend antes do POST para garantir o formato `+55XXXXXXXXXXX` — o GHL rejeita telefones sem DDI. O template tem o helper `LeadHelpers.NormalizePhoneBR()` no backend como fallback (idempotente: preserva o `+55` se já presente), mas normalizar no frontend evita ambiguidade.
361
+
362
+ ### 6. Gate de paridade — confirmar que ficou EXATAMENTE igual
363
+
364
+ Objetivo: **provar** que a LP migrada é idêntica à origem. Com o lift verbatim (passo 4), os checks determinísticos abaixo passam **por construção** — se algum falha, é sinal de que o lift introduziu drift naquela seção; voltar e corrigir o lift, não "ajustar até parecer".
365
+
366
+ **Hierarquia do gate:** os checks **determinísticos** (6b) são o **bloqueio**. O **screenshot diff** (6c) é **corroboração secundária** — nunca o bloqueio único, porque font hinting/antialiasing diferem entre ambientes mesmo com CSS byte-idêntico.
367
+
368
+ #### 6a. Boot + checklist manual (sanidade)
369
+
370
+ ```bash
371
+ docker compose up --build
372
+ ```
373
+
374
+ Percorrer a página em `http://localhost:3000` e conferir, lado a lado com a origem: todas as seções na ordem certa, textos/mídias corretos, formulário visível, navegação e footer funcionando. Isso é só uma sanidade rápida antes dos checks automáticos.
375
+
376
+ #### 6b. Checks determinísticos (o bloqueio)
377
+
378
+ A skill **scaffolda** o harness de paridade no projeto destino (o template **não** o traz): criar `frontend/e2e/parity.spec.ts` e `frontend/compare-sections.mjs`. O harness compara `http://localhost:3000` (destino) contra a origem (URL de produção ou um build local da origem) e **falha** se qualquer dimensão divergir:
379
+
380
+ 1. **Estrutura DOM + nomes de classe** por seção — `compare-sections.mjs` extrai o esqueleto de tags+classes de cada `<section>`/`<nav>`/`<footer>` dos dois lados (agrupado por chave semântica/texto âncora) e aponta diferenças. Adaptar o array `KEYS` (`[nome, matchFn]`) aos textos âncora das seções do projeto.
381
+ 2. **Estilo computado** em elementos amostrados — para cada seção, comparar `getComputedStyle` de elementos-chave (cor, `font-family`, `font-size`, `font-weight`, paddings/margins, `border-radius`) origem × destino. Divergência = falha.
382
+ 3. **Checksum de assets** — o conjunto de `sha256` de todas as imagens/vídeos/fontes servidas pelo destino deve igualar o conjunto capturado da origem no passo 1. Asset reencode/otimizado quebra aqui (proposital).
383
+ 4. **Comportamento por interação** — uma asserção Playwright por comportamento capturado no passo 1 (scroll-expand, carrossel, marquee, autoplay de vídeo, hover, máscara/validação de telefone, submit do formulário com `/api/leads` mockado retornando `{"ok":true}`).
384
+
385
+ ```bash
386
+ cd frontend
387
+ npx playwright test # roda parity.spec.ts + smoke (home.spec.ts do template)
388
+ node compare-sections.mjs http://localhost:3000/ https://origem-em-producao.com.br/
389
+ ```
390
+
391
+ #### 6c. Screenshot diff (corroboração secundária)
392
+
393
+ Captura full-page origem × destino em múltiplos viewports (mobile/tablet/desktop). Para não gerar ruído:
394
+ - **desabilitar animações** (`prefers-reduced-motion` / CSS de teste), **aguardar as web-fonts** (`document.fonts.ready`), **congelar vídeo/carrossel** num frame fixo;
395
+ - usar **tolerância ciente de antialiasing** (ex.: `maxDiffPixelRatio` pequeno, threshold de cor).
396
+
397
+ Um diff acima da tolerância é um **alerta para investigar** (provável seção com lift incompleto), não uma reprovação automática se os checks de 6b passam.
398
+
399
+ #### 6d. Critérios de aceite
400
+
401
+ A migração é "exata" quando **todos** são verdade:
402
+ - **6b determinístico passa**: DOM+classes, estilo computado, checksum de assets e comportamento batem com a origem em todas as seções.
403
+ - **6c screenshot diff** abaixo da tolerância em todos os breakpoints (mobile/tablet/desktop).
404
+ - Todas as seções renderizam com conteúdo real (não placeholders) e na ordem da origem.
405
+ - Formulário envia lead com sucesso para a integração configurada (testar com dado real em staging; `--demo-seed` popula a home com módulos de demonstração via `SEED_DEMO_CONTENT=true`, não envia lead de teste).
406
+ - Backoffice: editar um campo de texto, publicar — a mudança reflete na página (revalidação por tag ou ISR) **sem** alterar o markup/estilo renderizado.
407
+ - Nenhuma chave de API ou token aparece em arquivo versionado.
408
+
409
+ ## Anti-padrões
410
+
411
+ - Não commitar `.env` nem nenhum arquivo com tokens ou senhas.
412
+ - Não colocar credenciais de integração em variáveis de ambiente do container — elas pertencem ao nó `leadsConfig` no backoffice.
413
+ - Não criar módulos novos com alias que conflite com os do template (verificar `lpHome.config` e os `.config` existentes antes de nomear).
414
+ - Não usar `Guid.NewGuid()` em `SeedContentHandler` para o `Key` de um `mediaKey` que deve ser estável entre boots — calcular o GUID deterministicamente a partir do nome do arquivo ou recuperar o existente com `GetByKey`.
415
+ - Não saltear o smoke-boot (passo 3) — problemas de ambiente devem ser detectados antes de portar conteúdo.
416
+ - Não reutilizar `customHtmlSection` para tudo — módulos dedicados permitem edição granular no backoffice e ISR por tag.
417
+ - **Não reinterpretar layout/CSS nem "limpar" o markup** — portar verbatim; o módulo Umbraco só embrulha o mesmo markup da origem. Rebuild = drift.
418
+ - **Não reencodar nem otimizar assets** na migração — quebra a paridade de checksum (6b) e altera o render.
419
+ - **Não assumir o tema do template** — portar o Tailwind/`@theme` (e as fontes) da origem; a mesma classe utilitária renderiza diferente entre versões/temas.
420
+ - **Não tratar o screenshot diff como gate único** — o bloqueio são os checks determinísticos (6b); o diff visual é secundário.
421
+
422
+ ## Output esperado
423
+
424
+ - `docs/origem-inventario.md` — inventário de seções, conteúdo, mídia e identidade visual da LP de origem.
425
+ - `docs/mapeamento-modulos.md` — tabela de mapeamento seção → doctype/módulo com decisão (reuso direto / ajuste / lift verbatim).
426
+ - `backend/uSync/v17/ContentTypes/*.config` — novos doctypes para os módulos lifted da origem.
427
+ - `backend/Infrastructure/SeedContentHandler.cs` — seed idempotente com textos e mídia iniciais.
428
+ - `backend/seed-assets/` — assets de seed (imagens, vídeos copiados da origem).
429
+ - `frontend/src/components/modules/*.tsx` — componentes dos módulos novos.
430
+ - `frontend/src/components/modules/moduleRegistry.ts` — atualizado com novos aliases.
431
+ - `frontend/src/lib/types.ts` — tipos TypeScript para as props dos novos módulos.
432
+ - `frontend/src/app/globals.css` — tema portado verbatim da origem (paleta + tipografia + fontes).
433
+ - `frontend/e2e/home.spec.ts` — smoke test adaptado para o projeto.
434
+ - `frontend/e2e/parity.spec.ts` — **gerado pela skill**: gate determinístico (DOM+classes, estilo computado, checksum de assets, comportamento) + screenshot diff.
435
+ - `frontend/compare-sections.mjs` — **gerado pela skill**: comparação de esqueleto estrutural origem × destino.
436
+ - `.env` gerado pelo `new-lp.sh` / `new-lp.ps1` — **não versionado** (`.gitignore`).
437
+
438
+ ## Referências
439
+
440
+ Repo do template (privado, branch `master`): `polymorphism-tech/MORPH_Umbraco_LP_Template` — obtido automaticamente no passo 0.
441
+
442
+ - `MORPH_Umbraco_LP_Template/scripts/new-lp.sh` e `new-lp.ps1` — scaffold de nova LP
443
+ - `MORPH_Umbraco_LP_Template/backend/Lead/LeadComposer.cs` — registro dos sinks no DI
444
+ - `MORPH_Umbraco_LP_Template/backend/Lead/ILeadSink.cs` — interface de integração de leads
445
+ - `MORPH_Umbraco_LP_Template/backend/Controllers/LeadController.cs` — controller público + `ReadConfig()` + dispatch
446
+ - `MORPH_Umbraco_LP_Template/backend/Lead/GhlLeadSink.cs`, `ProspectProLeadSink.cs`, `WebhookLeadSink.cs` — sinks concretos
447
+ - `MORPH_Umbraco_LP_Template/backend/Infrastructure/SeedContentHandler.cs` — seed idempotente com mídia (MediaPicker3)
448
+ - `MORPH_Umbraco_LP_Template/backend/uSync/v17/ContentTypes/lpHome.config` — doctype raiz da home (Block List `modules`)
449
+ - `MORPH_Umbraco_LP_Template/frontend/src/components/modules/moduleRegistry.ts` — registro dos módulos
450
+ - `MORPH_Umbraco_LP_Template/frontend/src/app/globals.css` — bloco `@theme` para portar o tema da origem
451
+ - `MORPH_Umbraco_LP_Template/frontend/e2e/home.spec.ts` + `playwright.config.ts` — base de e2e (estender com `parity.spec.ts`)
452
+ - `MORPH_Umbraco_LP_Template/docker-compose.yml` — orquestração backend + frontend com variáveis de ambiente
@@ -4,7 +4,7 @@ description: Orquestra a execução autônoma de tasks de uma feature morph-spec
4
4
  version: core
5
5
  when-to-invoke: após Gate 2 aprovado, ao receber instrução "use /morph-apply" ou "implementar feature"
6
6
  user-invokable: true
7
- cliVersion: "7.4.0"
7
+ cliVersion: "7.5.0"
8
8
  ---
9
9
 
10
10
  # morph-apply
@@ -15,7 +15,7 @@ Executar todas as tasks pendentes de uma feature até `done`, aplicando `morph-e
15
15
 
16
16
  ## Quando invocar
17
17
 
18
- - Após Gate 2 aprovado (verificar `.morph/state.json → features[X].approvalGates.plan === true`).
18
+ - Após Gate 2 aprovado (verificar `.morph/features/{feature}/feature.json → approvalGates.plan.approved === true`).
19
19
  - Usuário pede "use /morph-apply {feature}" ou "implementar {feature}".
20
20
 
21
21
  ## Argumentos
@@ -26,7 +26,7 @@ Executar todas as tasks pendentes de uma feature até `done`, aplicando `morph-e
26
26
 
27
27
  - `.morph/features/{feature}/2-plan/tasks.json` existe e é válido (schema check).
28
28
  - `.morph/features/{feature}/2-plan/spec.md` e `mandate.md` existem.
29
- - Gate 2 está marcado aprovado no state.json (ou trust:auto está ativo).
29
+ - Gate 2 está marcado aprovado no `feature.json` da feature (ou trust:auto está ativo).
30
30
 
31
31
  ## Fluxo
32
32
 
@@ -86,11 +86,11 @@ while (true):
86
86
  (no attempt > 1: passar o feedback priorizado do morph-eval anterior como input)
87
87
  result = invoke Skill(morph-eval) with implementation output
88
88
  if result.score > best.score: best = result
89
- // Persista o score no state.json sob a task — torna o evalScore um signal de disco
90
- // no MOMENTO da implementação (não no fim). O hook gate-guard.js e o Gate 3 leem dali.
91
- // Edit em .morph/state.json é permitido. Grave:
92
- // features[feature].taskScores[task.id] = { lastScore: result.score, bestScore: best.score, attempts: attempt, updatedAt: <ISO> }
93
- edit .morph/state.json com o taskScores acima
89
+ // Persista o score no feature.json autoritativo da feature, sob taskScores — torna o
90
+ // evalScore um signal de disco no MOMENTO da implementação (não no fim). O hook
91
+ // gate-guard.js e o Gate 3 leem dali. Edit em .morph/features/** é permitido. Grave:
92
+ // taskScores[task.id] = { lastScore: result.score, bestScore: best.score, attempts: attempt, updatedAt: <ISO> }
93
+ edit .morph/features/{feature}/feature.json com o taskScores acima
94
94
 
95
95
  if result.score >= 9:
96
96
  break
@@ -159,16 +159,16 @@ Sinais de risco para Gate 3:
159
159
 
160
160
  Mesma lógica do Gate 1/Gate 2 via `shouldPauseGate(config, signals)`:
161
161
 
162
- **Persista a decisão em `state.json` ANTES de apresentar ou auto-aprovar** — grave
163
- `features[feature].gateDecisions.review = { pause, reason, signals, trust, decidedAt: <ISO> }`
164
- (o `signals.evalScore` aqui é o score composto do review-report). O hook `gate-guard.js` confere depois se a ação bateu. Sem o registro o guard não verifica.
162
+ **Persista a decisão no `feature.json` autoritativo da feature ANTES de apresentar ou auto-aprovar** — grave
163
+ `gateDecisions.review = { pause, reason, signals, trust, decidedAt: <ISO> }` em `.morph/features/{feature}/feature.json`
164
+ (o `signals.evalScore` aqui é o score composto do review-report; Edit em `.morph/features/**` é permitido). O hook `gate-guard.js` confere depois se a ação bateu. Sem o registro o guard não verifica.
165
165
 
166
166
  - Pause: `AskUserQuestion` com options `["aprovado, fechar feature", "preciso ajustar", "rollback"]`.
167
- - Auto: exibir summary do review-report e prosseguir.
167
+ - Auto: exibir summary do review-report, aprovar via `morph-spec approve {feature} review --mode auto`, e prosseguir.
168
168
 
169
169
  ### 6. Pós Gate 3
170
170
 
171
- - Atualizar `state.features[feature].status = "completed"` via state-sync (hook detecta "aprovado" em AskUserQuestion answer).
171
+ - Aprovar o Gate 3: em auto, `morph-spec approve {feature} review --mode auto`; em manual, o hook state-sync registra a resposta "aprovado" em AskUserQuestion. O gate fica gravado no `feature.json` autoritativo da feature — nunca via Edit à mão de `state.json`.
172
172
  - Sinalizar: "Feature {nome} concluída. Rode `/morph-archive {feature}` para arquivar."
173
173
  - Não arquivar automaticamente.
174
174
 
@@ -184,8 +184,8 @@ Mesma lógica do Gate 1/Gate 2 via `shouldPauseGate(config, signals)`:
184
184
  - Todas as tasks em `tasks.json` com status `done` ou `skipped`.
185
185
  - `.morph/features/{feature}/3-implement/recap.md` produzido por morph-implement.
186
186
  - `.morph/features/{feature}/4-review/review-report.md` produzido por morph-review.
187
- - Gate 3 marcado aprovado em `.morph/state.json` (via state-sync hook ou Edit direto).
188
- - `state.features[feature].status === "completed"`.
187
+ - Gate 3 registrado aprovado em `.morph/features/{feature}/feature.json` (autoritativo) — via `morph-spec approve` em auto, ou pelo hook state-sync no caminho manual.
188
+ - Feature com Gate 3 aprovado e pronta para `morph-spec archive` (que carimba `status: "completed"` + `archivedAt`).
189
189
 
190
190
  ## Referências
191
191
 
@@ -4,7 +4,7 @@ description: Entende o negócio, os usuários e a arquitetura antes de qualquer
4
4
  version: core
5
5
  when-to-invoke: ao receber um novo request de feature, antes de morph-plan
6
6
  user-invokable: true
7
- cliVersion: "7.4.0"
7
+ cliVersion: "7.5.0"
8
8
  ---
9
9
 
10
10
  # morph-brainstorming
@@ -87,9 +87,9 @@ O harness conhece o stack por padrão. Não pergunte sobre tecnologia a menos qu
87
87
  const riskList = getRiskSignals(signals);
88
88
  ```
89
89
 
90
- **Persista a decisão em `state.json` ANTES de apresentar ou auto-aprovar** — grave
91
- `features[feature].gateDecisions.proposal = { pause, reason, signals, trust, decidedAt: <ISO> }`
92
- (Edit em `.morph/state.json` é permitido). O hook `gate-guard.js` confere depois se a ação (pausar vs auto-aprovar) bateu com esta decisão e avisa em divergência. Sem o registro o guard não verifica — não pule.
90
+ **Persista a decisão no `feature.json` autoritativo da feature ANTES de apresentar ou auto-aprovar** — grave
91
+ `gateDecisions.proposal = { pause, reason, signals, trust, decidedAt: <ISO> }` em `.morph/features/{feature}/feature.json`
92
+ (Edit em `.morph/features/**` é permitido pela allowlist). O hook `gate-guard.js` confere depois se a ação (pausar vs auto-aprovar) bateu com esta decisão e avisa em divergência. Sem o registro o guard não verifica — não pule.
93
93
 
94
94
  **Se `pause === true`** (manual mode OU risco detectado em auto), use `AskUserQuestion`:
95
95
 
@@ -116,7 +116,7 @@ O harness conhece o stack por padrão. Não pergunte sobre tecnologia a menos qu
116
116
  **Riscos:** {principal} (sinais detectados: nenhum)
117
117
  ```
118
118
 
119
- O hook state-sync.js detecta a aprovação automaticamente. Em modo auto sem AskUserQuestion, a skill atualiza `.morph/state.json` via Edit direto (allowlist permite — ver settings.local.json).
119
+ No caminho manual, o hook state-sync.js detecta a aprovação automaticamente (grava o gate no `feature.json` autoritativo, `approvedBy: "manual"`). Em modo auto sem AskUserQuestion, aprove via `morph-spec approve {feature} proposal --mode auto` (ou `morph-spec advance {feature}` para já criar a pasta 2-plan/) — nunca via Edit à mão de state.json.
120
120
 
121
121
  ## Output esperado
122
122
  - `.morph/features/{feature}/0-proposal/proposal.md` — proposta aprovada
@@ -4,7 +4,7 @@ description: Avalia a qualidade de uma tarefa ou fase com rubricas graduadas 0-1
4
4
  version: core
5
5
  when-to-invoke: ao completar uma tarefa ou ao finalizar uma fase de implementação
6
6
  user-invokable: false
7
- cliVersion: "7.4.0"
7
+ cliVersion: "7.5.0"
8
8
  ---
9
9
 
10
10
  # morph-eval
@@ -4,7 +4,7 @@ description: Guia a execução de cada tarefa de implementação com standard e
4
4
  version: core
5
5
  when-to-invoke: ao iniciar cada task de implementação listada em tasks.json
6
6
  user-invokable: false
7
- cliVersion: "7.4.0"
7
+ cliVersion: "7.5.0"
8
8
  ---
9
9
 
10
10
  # morph-implement
@@ -109,9 +109,10 @@ Ao iniciar cada task de `tasks.json` — antes de escrever qualquer código. Nor
109
109
 
110
110
  `tasks.json` é protegido após o Gate 2, mas os campos de progresso (`status`,
111
111
  `outputs`, `notes`) continuam graváveis durante a fase implement. `writeTasksJson`
112
- grava o arquivo **inteiro** (Write) — esse é o caminho permitido. Nunca use a
113
- tool `Edit` em `tasks.json` após o Gate 2: o hook `protect-spec-files` bloqueia
114
- Edit e qualquer mudança em campos de plano (`title`, `dependencies`, `doneCriteria`…).
112
+ grava o arquivo inteiro (Write) e é o caminho mais simples; **uma `Edit` que mexe
113
+ só nesses campos mutáveis também é permitida** — o hook `protect-spec-files` faz
114
+ diff semântico e libera Write ou Edit desde que nenhum campo de contrato do plano
115
+ (`title`, `description`, `dependencies`, `effort`, `doneCriteria`, `group`) mude.
115
116
 
116
117
  9. Quando invocada por `morph-apply`, retornar o controle à skill orquestradora — ela decide qual task vem em seguida via `getReadyTasks(data)`. Quando invocada manualmente, verificar se há próxima task ready e iniciá-la imediatamente aplicando esta skill desde o passo 1 (auto-advance). Pause apenas se encontrar ambiguidade bloqueante não coberta pelo mandate.
117
118
 
@@ -4,7 +4,7 @@ description: Guia a produção dos três artefatos de planejamento de uma featur
4
4
  version: core
5
5
  when-to-invoke: ao iniciar a fase plan de uma feature, após o Gate 1 (proposal aprovado)
6
6
  user-invokable: true
7
- cliVersion: "7.4.0"
7
+ cliVersion: "7.5.0"
8
8
  ---
9
9
 
10
10
  # morph-plan
@@ -94,9 +94,9 @@ Ao iniciar a fase plan — após o `proposal.md` ser aprovado e antes de qualque
94
94
  const { pause, reason } = shouldPauseGate(config, signals);
95
95
  ```
96
96
 
97
- **Persista a decisão em `state.json` ANTES de apresentar ou auto-aprovar** — grave
98
- `features[feature].gateDecisions.plan = { pause, reason, signals, trust, decidedAt: <ISO> }`
99
- (Edit em `.morph/state.json` é permitido). Isso deixa o raciocínio de trust auditável no disco: o hook `gate-guard.js` confere depois se a ação tomada (pausar vs auto-aprovar) bateu com esta decisão e avisa em caso de divergência. Sem este registro o guard não tem como verificar — não pule.
97
+ **Persista a decisão no `feature.json` autoritativo da feature ANTES de apresentar ou auto-aprovar** — grave
98
+ `gateDecisions.plan = { pause, reason, signals, trust, decidedAt: <ISO> }` em `.morph/features/{feature}/feature.json`
99
+ (Edit em `.morph/features/**` é permitido pela allowlist). Isso deixa o raciocínio de trust auditável no disco: o hook `gate-guard.js` confere depois se a ação tomada (pausar vs auto-aprovar) bateu com esta decisão e avisa em caso de divergência. Sem este registro o guard não tem como verificar — não pule.
100
100
 
101
101
  **Se `pause === true`**, use `AskUserQuestion`:
102
102
 
@@ -121,14 +121,14 @@ Ao iniciar a fase plan — após o `proposal.md` ser aprovado e antes de qualque
121
121
  **Tasks:** {N} | Esforço total: {soma} | Caminho crítico: {IDs sem deps paralelas}
122
122
  ```
123
123
 
124
- O hook state-sync registra a aprovação automaticamente em answers de AskUserQuestion. Em auto sem AskUserQuestion, a skill edita `state.json` diretamente.
124
+ No caminho manual, o hook state-sync registra a aprovação automaticamente em answers de AskUserQuestion (grava o gate no `feature.json` autoritativo, `approvedBy: "manual"`). Em auto sem AskUserQuestion, aprove via `morph-spec approve {feature} plan --mode auto` (ou `morph-spec advance {feature}` para já criar a pasta da próxima fase) — nunca via Edit à mão de state.json.
125
125
 
126
126
  ## Output esperado
127
127
 
128
128
  - `.morph/features/{feature}/2-plan/spec.md` — especificação técnica completa
129
129
  - `.morph/features/{feature}/2-plan/mandate.md` — contrato de implementação
130
130
  - `.morph/features/{feature}/2-plan/tasks.json` — task list estruturada válida contra schema
131
- - Gate 2 marcado aprovado em `state.json`
131
+ - Gate 2 registrado aprovado em `.morph/features/{feature}/feature.json` (autoritativo)
132
132
 
133
133
  ## Referências
134
134