@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.
- package/README.md +2 -2
- package/bin/morph-spec.js +18 -0
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +9 -4
- package/framework/MORPH.md +4 -4
- package/framework/commands/create-umbraco-lp.md +35 -0
- package/framework/commands/migrate-lp-to-umbraco.md +33 -0
- package/framework/commands/morph-archive.md +17 -27
- package/framework/hooks/claude-code/core/enforce-phase-writes.js +115 -90
- package/framework/hooks/claude-code/core/protect-spec-files.js +32 -30
- package/framework/hooks/claude-code/core/state-sync.js +16 -48
- package/framework/hooks/shared/state-reader.js +74 -8
- package/framework/hooks/shared/state-writer.js +124 -0
- package/framework/rules/morph-task-tracking.md +69 -49
- package/framework/rules/morph-workflow.md +192 -191
- package/framework/skills/core/create-umbraco-lp/SKILL.md +252 -0
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +452 -0
- package/framework/skills/core/morph-apply/SKILL.md +15 -15
- package/framework/skills/core/morph-brainstorming/SKILL.md +5 -5
- package/framework/skills/core/morph-eval/SKILL.md +1 -1
- package/framework/skills/core/morph-implement/SKILL.md +5 -4
- package/framework/skills/core/morph-plan/SKILL.md +6 -6
- package/framework/skills/core/morph-proposal/SKILL.md +8 -7
- package/framework/skills/core/morph-review/SKILL.md +3 -3
- package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/templates/REGISTRY.json +1 -3
- package/framework/templates/context/CONTEXT-FEATURE.md +10 -17
- package/framework/templates/context/CONTEXT.md +181 -180
- package/framework/templates/docs/onboarding.md +122 -118
- package/framework/templates/docs/proposal.md +10 -0
- package/framework/templates/docs/spec.md +228 -228
- package/framework/templates/meta-prompts/parallel-workers/parallel-worker.md +80 -80
- package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +90 -89
- package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +2 -1
- package/framework/templates/meta-prompts/squad-leaders/squad-leader.md +44 -43
- package/framework/templates/meta-prompts/validators/checkpoint-validator.md +107 -107
- package/package.json +1 -1
- package/src/commands/project/update.js +415 -368
- package/src/commands/project/worktree.js +212 -210
- package/src/commands/state/advance.js +82 -0
- package/src/commands/state/archive.js +111 -0
- package/src/commands/validation/validate-feature.js +229 -105
- package/src/core/state/state-manager.js +238 -66
- package/src/dashboard/scanner/state-scanner.js +35 -8
- package/src/lib/generators/context-generator.js +12 -8
- 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.
|
|
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/
|
|
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
|
|
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
|
|
90
|
-
// no MOMENTO da implementação (não no fim). O hook
|
|
91
|
-
// Edit em .morph/
|
|
92
|
-
//
|
|
93
|
-
edit .morph/
|
|
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
|
|
163
|
-
`
|
|
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
|
-
-
|
|
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
|
|
188
|
-
- `
|
|
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.
|
|
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
|
|
91
|
-
`
|
|
92
|
-
(Edit em `.morph/
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
|
113
|
-
|
|
114
|
-
|
|
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.
|
|
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
|
|
98
|
-
`
|
|
99
|
-
(Edit em `.morph/
|
|
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
|
-
|
|
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
|
|
131
|
+
- Gate 2 registrado aprovado em `.morph/features/{feature}/feature.json` (autoritativo)
|
|
132
132
|
|
|
133
133
|
## Referências
|
|
134
134
|
|