@polymorphism-tech/morph-spec 7.5.0 → 7.6.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 (36) hide show
  1. package/README.md +2 -2
  2. package/claude-plugin.json +1 -1
  3. package/docs/CHEATSHEET.md +1 -1
  4. package/docs/QUICKSTART.md +1 -1
  5. package/framework/agents.json +1 -1
  6. package/framework/commands/morph-preflight.md +10 -5
  7. package/framework/rules/infrastructure-standards.md +4 -4
  8. package/framework/skills/core/create-umbraco-lp/SKILL.md +22 -2
  9. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +84 -5
  10. package/framework/skills/core/morph-apply/SKILL.md +1 -1
  11. package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
  12. package/framework/skills/core/morph-eval/SKILL.md +1 -1
  13. package/framework/skills/core/morph-implement/SKILL.md +1 -1
  14. package/framework/skills/core/morph-plan/SKILL.md +1 -1
  15. package/framework/skills/core/morph-proposal/SKILL.md +1 -1
  16. package/framework/skills/core/morph-review/SKILL.md +1 -1
  17. package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
  18. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  19. package/framework/standards/STANDARDS.json +80 -8
  20. package/framework/standards/ai-agents/durable-workflows-hitl.md +191 -0
  21. package/framework/standards/ai-agents/rag-custom-pgvector.md +246 -243
  22. package/framework/standards/ai-agents/setup.md +3 -0
  23. package/framework/standards/ai-agents/vector-data-extensions.md +170 -0
  24. package/framework/standards/ai-agents/workflows.md +190 -187
  25. package/framework/standards/infrastructure/docker/{easypanel-deploy.md → coolify-deploy.md} +49 -20
  26. package/framework/templates/README.md +7 -7
  27. package/framework/templates/REGISTRY.json +14 -14
  28. package/framework/templates/context/CONTEXT.md +3 -3
  29. package/framework/templates/docs/proposal.md +2 -2
  30. package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +1 -1
  31. package/framework/templates/infrastructure/docker/coolify.template.json +49 -0
  32. package/framework/templates/infrastructure/github/README.md +6 -6
  33. package/framework/templates/infrastructure/github/workflows/deploy-coolify.yml.hbs +33 -0
  34. package/package.json +1 -1
  35. package/framework/templates/infrastructure/docker/easypanel.template.json +0 -46
  36. package/framework/templates/infrastructure/github/workflows/deploy-easypanel.yml.hbs +0 -31
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  > Spec-driven development framework for multi-stack projects. Turns feature requests into implementation-ready code through structured, AI-orchestrated phases.
4
4
 
5
5
  **Package:** `@polymorphism-tech/morph-spec`
6
- **Version:** 7.5.0
6
+ **Version:** 7.6.0
7
7
  **Requires:** Node.js 22+, Claude Code
8
8
 
9
9
  ---
@@ -365,4 +365,4 @@ Code generated by morph-spec (contracts, templates, implementation output) belon
365
365
 
366
366
  ---
367
367
 
368
- *morph-spec v7.5.0 by [Polymorphism Tech](https://polymorphism.tech)*
368
+ *morph-spec v7.6.0 by [Polymorphism Tech](https://polymorphism.tech)*
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "morph-spec",
3
- "version": "7.5.0",
3
+ "version": "7.6.0",
4
4
  "displayName": "MORPH-SPEC Framework",
5
5
  "description": "Spec-driven development with 38 agents and 8-phase workflow for .NET/Blazor/Next.js/Azure",
6
6
  "publisher": "polymorphism-tech",
@@ -196,4 +196,4 @@ These files are never edited directly. Use CLI commands or `morph-spec update` i
196
196
 
197
197
  ---
198
198
 
199
- *morph-spec v7.5.0 by Polymorphism Tech*
199
+ *morph-spec v7.6.0 by Polymorphism Tech*
@@ -197,4 +197,4 @@ morph-spec doctor
197
197
 
198
198
  ---
199
199
 
200
- *morph-spec v7.5.0 by Polymorphism Tech*
200
+ *morph-spec v7.6.0 by Polymorphism Tech*
@@ -111,7 +111,7 @@
111
111
  "stack": ["dotnet"],
112
112
  "standards": [
113
113
  "backend-integrations-hangfire-hangfire-jobs",
114
- "infrastructure-docker-easypanel-deploy",
114
+ "infrastructure-docker-coolify-deploy",
115
115
  "backend-dotnet-scrutor"
116
116
  ],
117
117
  "persona": "Você cuida de infra: jobs assíncronos com Hangfire (apenas quando há razão real), deployment, observabilidade. Pensa em retry, idempotência, dead-letter, métricas. Não inventa infra — usa o que o stack já oferece."
@@ -1,12 +1,12 @@
1
1
  ---
2
- description: Validate project readiness before deploy — checks specs, contracts, tests, Docker, Neon/PostgreSQL, and EasyPanel configuration
2
+ description: Validate project readiness before deploy — checks specs, contracts, tests, Docker, Neon/PostgreSQL, and Coolify configuration
3
3
  argument-hint: [feature-name]
4
4
  allowed-tools: Read, Bash, Glob, Grep
5
5
  ---
6
6
 
7
7
  # Pre-Flight Check for Deployment
8
8
 
9
- Validates project readiness before deploying to Docker/EasyPanel + Neon/PostgreSQL.
9
+ Validates project readiness before deploying to Docker/Coolify + Neon/PostgreSQL.
10
10
 
11
11
  ## Usage
12
12
 
@@ -153,10 +153,10 @@ test -f .env.example && echo "OK" || echo "MISSING .env.example"
153
153
 
154
154
  ---
155
155
 
156
- ### 6. EasyPanel Configuration
156
+ ### 6. Coolify Configuration
157
157
 
158
158
  **What it checks:**
159
- - Services correctly defined for EasyPanel
159
+ - Application correctly defined in Coolify (source repo, Dockerfile path, port)
160
160
  - Health check endpoints exist (`/health` or `/healthz`)
161
161
  - Port mappings match Dockerfile EXPOSE
162
162
 
@@ -164,8 +164,13 @@ test -f .env.example && echo "OK" || echo "MISSING .env.example"
164
164
  ```bash
165
165
  # Verify health endpoint exists in code
166
166
  grep -rn "MapGet.*health" src/ --include="*.cs"
167
+
168
+ # Verify Coolify config placeholders were filled in (local file only)
169
+ grep -E "REPLACE_WITH_" coolify.json 2>/dev/null && echo "✗ coolify.json still has unfilled placeholders" || echo "✓ coolify.json configured"
167
170
  ```
168
171
 
172
+ > Coolify has no MCP/CLI — its API is only reachable via SSH into the VPS. `morph-preflight` never does this itself; it only checks the project's local files. Live verification against the running instance is a manual step for whoever holds VPS access.
173
+
169
174
  ---
170
175
 
171
176
  ## Example Output
@@ -178,7 +183,7 @@ MORPH Pre-Flight Check: Deployment
178
183
  Dockerfile: Valid multi-stage build (API + Web)
179
184
  Docker Compose: Valid configuration
180
185
  Secrets: No hardcoded credentials found
181
- EasyPanel: Health endpoint found at /healthz
186
+ Coolify: Health endpoint found at /healthz
182
187
 
183
188
  Summary: 6 passed, 0 failed, 0 warnings
184
189
 
@@ -3,14 +3,14 @@ paths:
3
3
  - "**/Dockerfile*"
4
4
  - "**/docker-compose*"
5
5
  - "**/.github/**"
6
- - "**/easypanel*"
6
+ - "**/coolify*"
7
7
  stacks:
8
8
  - docker
9
9
  ---
10
10
 
11
11
  # Infrastructure Standards
12
12
 
13
- @.morph/framework/standards/infrastructure/docker/easypanel-deploy.md
13
+ @.morph/framework/standards/infrastructure/docker/coolify-deploy.md
14
14
 
15
15
  ## Infrastructure Templates
16
16
 
@@ -19,9 +19,9 @@ stacks:
19
19
  | .NET API Dockerfile | `.morph/framework/templates/infrastructure/docker/dockerfile-api.dockerfile` | Multi-stage .NET |
20
20
  | Next.js Dockerfile | `.morph/framework/templates/infrastructure/docker/dockerfile-web.dockerfile` | Multi-stage Next.js |
21
21
  | Docker Compose | `.morph/framework/templates/infrastructure/docker/docker-compose.template.yml` | Local dev env |
22
- | EasyPanel Config | `.morph/framework/templates/infrastructure/docker/easypanel.template.json` | Deployment config |
22
+ | Coolify Config | `.morph/framework/templates/infrastructure/docker/coolify.template.json` | Deployment config |
23
23
  | .NET Build CI | `.morph/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs` | CI workflow |
24
24
  | Docker Push CI | `.morph/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs` | Build & push |
25
- | Deploy EasyPanel | `.morph/framework/templates/infrastructure/github/workflows/deploy-easypanel.yml.hbs` | CD webhook |
25
+ | Deploy Coolify | `.morph/framework/templates/infrastructure/github/workflows/deploy-coolify.yml.hbs` | CD via SSH |
26
26
 
27
27
  Read the template and adapt to your project structure.
@@ -4,7 +4,7 @@ description: Guia a criação de uma landing page nova ponta-a-ponta a partir do
4
4
  version: core
5
5
  when-to-invoke: ao iniciar um novo projeto de LP com o template Umbraco genérico
6
6
  user-invokable: true
7
- cliVersion: "7.5.0"
7
+ cliVersion: "7.6.0"
8
8
  ---
9
9
 
10
10
  # create-umbraco-lp
@@ -82,17 +82,31 @@ UMBRACO_ADMIN_PASSWORD=<gerada>
82
82
  UMBRACO_GLOBAL_ID=<uuid-gerado>
83
83
  ALLOWED_ORIGINS=http://localhost:3000
84
84
  REVALIDATE_SECRET=<gerado>
85
+ FRONTEND_URL=http://localhost:3000
85
86
  LEADS_INTEGRATION=<integração-escolhida>
86
87
  ```
87
88
 
89
+ `FRONTEND_URL` (junto com `REVALIDATE_SECRET`) alimenta o `RevalidateOnPublishHandler` do
90
+ backend: a cada publish no Umbraco, ele chama `/api/revalidate` do frontend sozinho, em
91
+ código — não depende de configurar nada em Settings > Webhooks no backoffice. **Em
92
+ produção, atualize `FRONTEND_URL` para o domínio público real do frontend.**
93
+
88
94
  E gera `backend/appsettings.Development.json` com o bloco Unattended install do Umbraco (usando o email/senha computados pelo script). Isso significa que um projeto recém-scaffoldado **auto-instala no `dotnet run`** — sem necessidade de rodar o instalador UI manualmente.
89
95
 
90
96
  E escreve `frontend/.env.local` com:
91
97
  ```
92
98
  NEXT_PUBLIC_UMBRACO_URL=https://localhost:44324
99
+ NEXT_PUBLIC_SITE_URL=http://localhost:3000
93
100
  REVALIDATE_SECRET=<mesmo-secret>
94
101
  ```
95
102
 
103
+ `NEXT_PUBLIC_SITE_URL` é usada por `robots.ts`/`sitemap.ts`. **Em produção, precisa ser
104
+ setada como Build Variable no Coolify** — o `Dockerfile` do template
105
+ declara `ARG NEXT_PUBLIC_SITE_URL` (e `ARG NEXT_PUBLIC_UMBRACO_URL`) justamente para isso;
106
+ sem marcar como Build Variable, a env var não chega ao `next build` e o `robots.txt`/
107
+ `sitemap.xml` publicam o domínio placeholder do template (`minha-landing-page.com.br`) em
108
+ produção — já aconteceu num projeto real.
109
+
96
110
  > **Nota de overwrite:** se `.env` já existe, `new-lp.ps1` prompta (Read-Host) para sobrescrever — portanto, uma execução não-interativa em um diretório que já tem `.env` vai bloquear na prompt. Execute em um scaffold fresco, ou responda à prompt interativamente.
97
111
 
98
112
  > **Nota importante:** `ALLOWED_ORIGINS` é gravado com `http://localhost:3000` pelo script independentemente do que foi informado no passo 1. Se o usuário especificou um domínio de produção, a forma de aplicá-lo depende do modo de execução:
@@ -229,9 +243,15 @@ Antes de fazer deploy ou entregar o projeto ao cliente:
229
243
  - [ ] Delivery API responde com o node `lpHome` populado: `/umbraco/delivery/api/v2/content?contentType=lpHome&take=1`.
230
244
  - [ ] `.env` contém `ALLOWED_ORIGINS` com o domínio de produção correto (separado por vírgula se múltiplos).
231
245
  - [ ] `UMBRACO_GLOBAL_ID` é único para este projeto (gerado pelo script — não reutilizar de outro projeto).
232
- - [ ] SEO: preencher título, descrição e OG image no backoffice (campos do node `lpHome`).
246
+ - [ ] SEO: preencher no backoffice, aba SEO do node `lpHome` — Page Title, Meta Title, Meta Description, **Social Image** (1200x630, sem ela não há `og:image` e o link não gera preview no WhatsApp/redes), Canonical Url (só a URL, sem tag HTML).
247
+ - [ ] Favicon: preencher o campo **Favicon** no backoffice (aba Configuration) — `app/icon.tsx`/`apple-icon.tsx` já leem esse campo dinamicamente; sem ele, cai no favicon estático genérico do template.
248
+ - [ ] `NEXT_PUBLIC_SITE_URL` **e** `NEXT_PUBLIC_UMBRACO_URL` setadas como **Build Variable** no provedor (não só runtime) — conferir `robots.txt`/`sitemap.xml` em produção com o domínio real após o primeiro deploy.
249
+ - [ ] `FRONTEND_URL` + `REVALIDATE_SECRET` setadas no backend em produção (revalidação automática pós-publish via `RevalidateOnPublishHandler`) — testar: publicar algo no backoffice e conferir no log do backend a linha `RevalidateOnPublish: cache do frontend invalidado após publish.`
250
+ - [ ] No Index: confirmar que está **desligado** no backoffice antes de ir pra produção (o campo é opt-in pra esconder do Google, não deveria vir ligado por padrão).
233
251
  - [ ] Variáveis sensíveis (`UMBRACO_ADMIN_PASSWORD`, `REVALIDATE_SECRET`, tokens de integração) não estão commitadas no repositório (`.env` deve estar no `.gitignore`).
234
252
  - [ ] Em produção com Docker Compose: `docker-compose up --build` e confirmar que backend e frontend sobem com as variáveis do `.env`.
253
+ - [ ] `browserslist` presente em `frontend/package.json` — já vem do template; só confirmar que não foi removido/sobrescrito durante a customização.
254
+ - [ ] Se o cliente vai subir fotos de câmera/celular pelo Media Picker (galeria, hero, etc.): confirmar que `ResizeLargeMediaUploadsHandler` está registrado em `backend/Program.cs` (já vem do template) — sem ele, upload cru de 10+ MB pode virar 500 intermitente sob concorrência no otimizador do Next. Setar `MEDIA_BACKFILL_SECRET` em produção se for preciso reprocessar mídia já publicada (ver seção "Mídia" do README do template).
235
255
 
236
256
  > **Nota Docker — `LEADS_INTEGRATION` e `SEED_DEMO_CONTENT`:** o `docker-compose.yml` **não** injeta essas duas variáveis no container do backend. Mesmo que estejam no `.env`, o seed sempre inicializa `leadIntegration` como `"none"` no boot Docker. Isso é esperado: o editor deve configurar a integração manualmente no backoffice após o primeiro boot. Se precisar seeding automático da integração via Docker, adicione a variável na seção `environment` do serviço `backend` em `docker-compose.yml` manualmente.
237
257
 
@@ -4,7 +4,7 @@ description: Migra uma landing page existente (React/Vite, Next.js estático ou
4
4
  version: core
5
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
6
  user-invokable: true
7
- cliVersion: "7.5.0"
7
+ cliVersion: "7.6.0"
8
8
  ---
9
9
 
10
10
  # migrate-lp-to-umbraco
@@ -100,6 +100,16 @@ O objetivo desta etapa não é "entender" a LP, é **capturar tudo o que é nece
100
100
  - Registrar o `sha256` de cada asset (vira a base da paridade de checksum no passo 6).
101
101
  - **Não reencodar nem otimizar** nenhum asset — qualquer reencode quebra a paridade.
102
102
 
103
+ **SEO/GEO da origem — capturar para semear, não perder no lift:** a paridade visual não
104
+ cobre isso sozinha — um lift perfeito que esquece o SEO da origem entrega uma LP idêntica
105
+ visualmente mas invisível pra buscadores/IA no dia 1. Capturar da origem:
106
+ - `<title>`, `<meta name="description">`, `<link rel="canonical">`.
107
+ - Open Graph: `og:title`, `og:description`, `og:image` (baixar a imagem, idealmente 1200x630).
108
+ - JSON-LD (`<script type="application/ld+json">`) — schema completo, se existir.
109
+ - Favicon (todos os tamanhos/formatos disponíveis) e `apple-touch-icon`.
110
+ - `robots.txt` e `sitemap.xml` da origem — confirmar se há regras além do default (bots de IA bloqueados? outras URLs?).
111
+ - `llms.txt` da origem, se existir.
112
+
103
113
  **Formulário de captação** — campos presentes, validação no frontend, endpoint atual, integração de CRM/leads.
104
114
 
105
115
  **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.
@@ -268,7 +278,22 @@ sectionNode.SetValue("videoFundo", mediaJson);
268
278
 
269
279
  Coloque os assets de seed em `backend/seed-assets/` (imagens, vídeos, ícones da LP de origem).
270
280
 
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.
281
+ **Semear também o SEO/GEO capturado no passo 1** — não deixar em branco pra "o editor preencher depois" (isso já causou um site indo pro ar sem `og:image`, com favicon genérico e apontando pro domínio errado no sitemap num projeto real):
282
+
283
+ ```csharp
284
+ content.SetValue("pageTitleSeo", "<título da origem>");
285
+ content.SetValue("titleSeo", "<og:title da origem>");
286
+ content.SetValue("descriptionSeo", "<meta description da origem>");
287
+ content.SetValue("canonicalUrlSeo", "<URL canônica de produção>"); // só a URL, sem tag HTML
288
+ content.SetValue("noIndexSeo", false); // é Mandatory=false a partir do fix de 2026-07-02 — não deixar true
289
+ content.SetValue("jsonLDSchemaSeo", OrigemJsonLd); // JSON-LD capturado da origem, se existir
290
+ // imageSeo (og:image) e favicon: importar como media (mesmo padrão de CreateOrGetMedia)
291
+ // e setar via MediaPicker3, igual ao padrão de mídia acima.
292
+ ```
293
+
294
+ `imageSeo` (1200x630) e `favicon` alimentam `app/icon.tsx`/`apple-icon.tsx` e o `<meta property="og:image">` automaticamente — não precisam de nenhum código extra no frontend, só o seed correto.
295
+
296
+ **O que vai no SeedContentHandler:** textos de inauguração, mídia local (arquivos em `seed-assets/`), ordem das seções, configuração inicial, **e o SEO/GEO capturado no passo 1**. O conteúdo fica editável no backoffice após o primeiro boot — o seed é apenas o estado inicial.
272
297
 
273
298
  **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
299
 
@@ -298,12 +323,58 @@ Copiar as variáveis de design da origem para `frontend/src/app/globals.css` no
298
323
  }
299
324
  ```
300
325
 
301
- Atualizar os `<link>` de Google Fonts em `frontend/src/app/layout.tsx` para carregar as fontes corretas.
326
+ **Fontes — auto-hospedar, nunca `<link>` externo pro Google Fonts:** um `<link rel="stylesheet"
327
+ href="https://fonts.googleapis.com/css2?...">` no `<head>` é render-blocking (round-trip
328
+ externo antes de qualquer paint) e, combinado com `font-display: swap`, causa CLS quando a
329
+ fonte troca depois do primeiro paint — foi 1.680ms de render-blocking estimado e 0.221 de CLS
330
+ medidos num caso real (Barone_LP). **Não usar `next/font/google`** aqui (diferente do passo 4
331
+ de `create-umbraco-lp`, que pode usar): o nome de família que ele gera é escopado/diferente do
332
+ literal da origem, e o gate de paridade (passo 6b.2) compara `font-family` computado
333
+ byte-a-byte contra a origem — `next/font` quebra esse check.
334
+
335
+ O fix correto, que preserva a paridade:
336
+ 1. Buscar a CSS real do Google pra descobrir as URLs `.woff2` (`curl` na mesma URL que iria no
337
+ `<link>`) — geralmente **um arquivo por família+estilo já cobre todos os pesos estáticos**
338
+ usados (Google costuma reaproveitar o mesmo arquivo pra weight 400/500/600/700 de uma fonte
339
+ não-variável). Baixar só o subset `latin` (`unicode-range: U+0000-00FF, ...`) — cobre toda
340
+ acentuação do português (á, ã, ç, é... estão em U+00C0-00FF).
341
+ 2. Salvar os `.woff2` em `frontend/public/fonts/`.
342
+ 3. Declarar `@font-face` em `globals.css` (ou no arquivo de tema) com o **mesmo `font-family`
343
+ literal da origem** (`'Cormorant'`, `'Jost'`, etc. — não o nome gerado por uma lib) e
344
+ `font-display: optional` (não `swap`) — `optional` só troca pro custom font se ele chegar
345
+ quase instantaneamente; senão fica no fallback pra aquela visita, evitando o reflow visível
346
+ que gerou o CLS.
347
+ 4. `<link rel="preload" href="/fonts/arquivo.woff2" as="font" type="font/woff2" crossOrigin="anonymous">`
348
+ no `<head>` do `layout.tsx`, sem nenhum `<link>` pro domínio do Google.
349
+
350
+ Verificar ao final: `getComputedStyle(el).fontFamily` no elemento de heading do hero deve
351
+ retornar **a mesma string literal que a origem** (ex.: `"Cormorant, Georgia, serif"`) — só a
352
+ origem do arquivo mudou (de `fonts.gstatic.com` pra same-origin), não o nome da família.
302
353
 
303
354
  #### 4d. Componentes frontend dos módulos novos
304
355
 
305
356
  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
357
 
358
+ > **Exceção deliberada ao verbatim — imagens de mídia do Umbraco:** o componente `Picture`
359
+ > lifted da origem provavelmente emite um `<img>` cru. Pra qualquer `src` que vier do Media
360
+ > Picker (URL do backend, editável no backoffice — diferente de um asset estático em
361
+ > `/public`), usar `next/image` (`fill`, com o container já dimensionado) desde o primeiro
362
+ > lift, não um `<img>` cru. Isso **não** quebra o gate de paridade (passo 6b.3 só faz checksum
363
+ > de assets em `/public`, nunca de URLs de mídia) — pelo contrário, sem isso qualquer foto que
364
+ > o cliente trocar depois no backoffice chega crua (fotos de câmera/celular fácil 10+ MB) e
365
+ > vira lento ou 500 sob concorrência (ver seção "Mídia" no README do template).
366
+ >
367
+ > **Pitfall a checar nesse lift:** `next/image fill` aplica `position/height/width` **inline**
368
+ > no `<img>` renderizado — isso vence qualquer regra de classe (`.minha-grade img { height:
369
+ > 116%; margin-top: -8% }`, por exemplo) que a origem use pra efeitos de oversize/parallax/zoom
370
+ > em cima de um `<img>` cru, mesmo que a regra continue no CSS lifted verbatim. Se a origem tem
371
+ > algum truque desses (imagem deliberadamente maior que o container + margin negativo, comum
372
+ > em seções com parallax scroll-scrub), a troca pra `fill` abre um vão visível onde a "sobra"
373
+ > da imagem deveria estar. Fix: `!important` na propriedade de classe que está sendo vencida
374
+ > (normalmente só `height`) — é uma das poucas justificativas legítimas pra `!important`, já
375
+ > que é o único jeito de vencer um inline style que você não controla diretamente. Checar isso
376
+ > na hora do lift, não esperar aparecer como bug visual depois.
377
+
307
378
  ```tsx
308
379
  // Padrão: componente com 'use client', recebe { data: SectionProps },
309
380
  // faz cast para o tipo tipado, e lê props via data.property.
@@ -365,6 +436,12 @@ Objetivo: **provar** que a LP migrada é idêntica à origem. Com o lift verbati
365
436
 
366
437
  **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
438
 
439
+ > Os dois pontos do passo 4c (fontes auto-hospedadas) e 4d (`next/image fill` pra mídia do
440
+ > Umbraco) não são drift de paridade — são a paridade **certa** pedindo um detalhe extra de
441
+ > implementação. O checksum de assets (6b.3) só cobre `/public`; o estilo computado (6b.2)
442
+ > continua batendo porque `font-family` fica literal. Nenhum dos dois deveria fazer o gate
443
+ > falhar quando bem aplicados.
444
+
368
445
  #### 6a. Boot + checklist manual (sanidade)
369
446
 
370
447
  ```bash
@@ -403,7 +480,9 @@ A migração é "exata" quando **todos** são verdade:
403
480
  - **6c screenshot diff** abaixo da tolerância em todos os breakpoints (mobile/tablet/desktop).
404
481
  - Todas as seções renderizam com conteúdo real (não placeholders) e na ordem da origem.
405
482
  - 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.
483
+ - Backoffice: editar um campo de texto, publicar — a mudança reflete na página **em produção, sem precisar de deploy manual** (o `RevalidateOnPublishHandler` do backend chama `/api/revalidate` sozinho; só funciona com `FRONTEND_URL`/`REVALIDATE_SECRET` setados) e **sem** alterar o markup/estilo renderizado.
484
+ - SEO/GEO da origem migrado, não perdido: `curl` a home de produção e conferir `<title>`, `<meta description>`, `og:image`, canonical e JSON-LD batendo com o que foi capturado no passo 1 — e que o favicon do browser não é o ícone genérico do template.
485
+ - `robots.txt`/`sitemap.xml` de produção apontam pro domínio real (não o placeholder `minha-landing-page.com.br` do template) — só acontece se `NEXT_PUBLIC_SITE_URL` foi setada como Build Variable no provedor.
407
486
  - Nenhuma chave de API ou token aparece em arquivo versionado.
408
487
 
409
488
  ## Anti-padrões
@@ -415,7 +494,7 @@ A migração é "exata" quando **todos** são verdade:
415
494
  - Não saltear o smoke-boot (passo 3) — problemas de ambiente devem ser detectados antes de portar conteúdo.
416
495
  - Não reutilizar `customHtmlSection` para tudo — módulos dedicados permitem edição granular no backoffice e ISR por tag.
417
496
  - **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.
497
+ - **Não reencodar nem otimizar assets da origem** (o que vai pra `/public`/`seed-assets`) na migração — quebra a paridade de checksum (6b) e altera o render. Isso **não** se aplica a imagens que vêm do Media Picker do Umbraco (ver passo 4d) — essas não são asset da origem, não entram no checksum, e otimizá-las via `next/image` é o comportamento correto, não uma violação de paridade.
419
498
  - **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
499
  - **Não tratar o screenshot diff como gate único** — o bloqueio são os checks determinísticos (6b); o diff visual é secundário.
421
500
 
@@ -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.5.0"
7
+ cliVersion: "7.6.0"
8
8
  ---
9
9
 
10
10
  # morph-apply
@@ -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.5.0"
7
+ cliVersion: "7.6.0"
8
8
  ---
9
9
 
10
10
  # morph-brainstorming
@@ -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.5.0"
7
+ cliVersion: "7.6.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.5.0"
7
+ cliVersion: "7.6.0"
8
8
  ---
9
9
 
10
10
  # morph-implement
@@ -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.5.0"
7
+ cliVersion: "7.6.0"
8
8
  ---
9
9
 
10
10
  # morph-plan
@@ -4,7 +4,7 @@ description: Orquestra a fase de proposta+plano de uma feature morph-spec. Encad
4
4
  version: core
5
5
  when-to-invoke: ao receber instrução "use /morph-proposal" do usuário, ou ao iniciar uma nova feature
6
6
  user-invokable: true
7
- cliVersion: "7.5.0"
7
+ cliVersion: "7.6.0"
8
8
  ---
9
9
 
10
10
  # morph-proposal
@@ -4,7 +4,7 @@ description: Conduz a fase de revisão e produz o review-report.md para o Gate 3
4
4
  version: core
5
5
  when-to-invoke: ao iniciar a fase review, após todas as tasks de tasks.json estarem com status `done`
6
6
  user-invokable: true
7
- cliVersion: "7.5.0"
7
+ cliVersion: "7.6.0"
8
8
  ---
9
9
 
10
10
  # morph-review
@@ -4,7 +4,7 @@ description: Varre o codebase existente, detecta stack atual, compara com o stac
4
4
  version: core
5
5
  when-to-invoke: ao iniciar uma feature em projeto brownfield (codebase já existente)
6
6
  user-invokable: true
7
- cliVersion: "7.5.0"
7
+ cliVersion: "7.6.0"
8
8
  ---
9
9
 
10
10
  # morph-stack-scan
@@ -4,7 +4,7 @@ description: Localiza e carrega um standard específico do STANDARDS.json sob de
4
4
  version: core
5
5
  when-to-invoke: quando precisar consultar um standard específico durante a implementação
6
6
  user-invokable: true
7
- cliVersion: "7.5.0"
7
+ cliVersion: "7.6.0"
8
8
  ---
9
9
 
10
10
  # morph-standards
@@ -1495,9 +1495,9 @@
1495
1495
  "digest": "Vitest for unit tests, React Testing Library for component tests. Mock next/navigation with vi.mock. Test Server Actions with direct function calls. Use MSW for API mocking in component tests. Playwright for E2E tests against running app. Never test implementation details — test user behavior and output."
1496
1496
  },
1497
1497
  {
1498
- "id": "infrastructure-docker-easypanel-deploy",
1499
- "name": "easypanel deploy",
1500
- "path": "infrastructure/docker/easypanel-deploy.md",
1498
+ "id": "infrastructure-docker-coolify-deploy",
1499
+ "name": "coolify deploy",
1500
+ "path": "infrastructure/docker/coolify-deploy.md",
1501
1501
  "category": "infrastructure",
1502
1502
  "subcategory": "docker",
1503
1503
  "tags": [
@@ -1508,17 +1508,20 @@
1508
1508
  "docker"
1509
1509
  ],
1510
1510
  "aliases": [
1511
- "easypanel",
1511
+ "coolify",
1512
1512
  "docker deploy",
1513
1513
  "self-hosted",
1514
1514
  "VPS deployment",
1515
- "docker compose"
1515
+ "docker compose",
1516
+ "coolify api",
1517
+ "ssh deploy"
1516
1518
  ],
1517
1519
  "anchors": {
1518
- "setup": "#easypanel-service-config",
1519
- "deployment": "#zero-downtime-deploys"
1520
+ "setup": "#coolify-service-config",
1521
+ "deployment": "#zero-downtime-deploys",
1522
+ "ssh": "#triggering-a-deploy-via-ssh--api"
1520
1523
  },
1521
- "digest": "Create app in EasyPanel from container registry image. Configure environment variables in EasyPanel UI — not in Dockerfile. Use EasyPanel's managed Postgres/Redis for databases. Configure /health endpoint for health checks. Enable HTTPS via Let's Encrypt automatically. Use EasyPanel volumes for persistent storage."
1524
+ "digest": "Coolify is the self-hosted Docker PaaS. No MCP/CLI exists — all access is via SSH into the VPS, hitting the Coolify API at localhost:8000/api/v1 with a server-side token (never printed, never leaves the SSH session). Configure environment variables in Coolify UI/API — not in Dockerfile. Configure /health endpoint for health checks. HTTPS via Let's Encrypt automatic (Traefik). Redeploys trigger via GET /deploy?uuid={uuid} over SSH."
1522
1525
  },
1523
1526
  {
1524
1527
  "id": "infrastructure-neon-setup",
@@ -1657,6 +1660,75 @@
1657
1660
  "edit-workflows": "#iterative-editing-workflows"
1658
1661
  },
1659
1662
  "digest": "Guidelines for generating project images with Gemini Image MCP — prompt patterns, aspect ratios, design-system-aware prompts, iterative editing, and assets.md format."
1663
+ },
1664
+ {
1665
+ "id": "ai-agents-durable-workflows-hitl",
1666
+ "name": "durable workflows hitl",
1667
+ "path": "ai-agents/durable-workflows-hitl.md",
1668
+ "category": "ai-agents",
1669
+ "subcategory": null,
1670
+ "tags": [
1671
+ "ai-agents",
1672
+ "maf",
1673
+ "workflow",
1674
+ "durability"
1675
+ ],
1676
+ "stacks": [
1677
+ "dotnet"
1678
+ ],
1679
+ "aliases": [
1680
+ "durable workflow",
1681
+ "checkpoint",
1682
+ "checkpointing",
1683
+ "human in the loop",
1684
+ "hitl",
1685
+ "requestport",
1686
+ "requestinfoevent",
1687
+ "resume workflow",
1688
+ "long running workflow",
1689
+ "approval workflow",
1690
+ "icheckpointstore"
1691
+ ],
1692
+ "anchors": {
1693
+ "when": "#quando-usar-este-standard",
1694
+ "hitl": "#human-in-the-loop-via-requestport",
1695
+ "checkpointing": "#checkpointing--durável-sem-depender-de-azure",
1696
+ "durable-task": "#azure-durable-task-extension--escape-hatch-not-default"
1697
+ },
1698
+ "digest": "HITL via RequestPort — a workflow node that pauses and waits for an external (human) response via RequestInfoEvent. Checkpointing is GA and Azure-free: CheckpointManager.CreateInMemory() for tests, CreateJson(FileSystemJsonCheckpointStore) for local-disk durability, or a custom ICheckpointStore (recommended against the project's own Postgres). Microsoft.Agents.AI.DurableTask (Azure Durable Task Scheduler) is documented as an escape hatch only, not adopted as default."
1699
+ },
1700
+ {
1701
+ "id": "ai-agents-vector-data-extensions",
1702
+ "name": "vector data extensions",
1703
+ "path": "ai-agents/vector-data-extensions.md",
1704
+ "category": "ai-agents",
1705
+ "subcategory": null,
1706
+ "tags": [
1707
+ "ai-agents",
1708
+ "maf",
1709
+ "rag",
1710
+ "vector-data"
1711
+ ],
1712
+ "stacks": [
1713
+ "dotnet"
1714
+ ],
1715
+ "aliases": [
1716
+ "microsoft extensions vectordata",
1717
+ "vectorstorecollection",
1718
+ "vector store abstraction",
1719
+ "data ingestion",
1720
+ "ingestion pipeline",
1721
+ "semantic kernel connectors exception",
1722
+ "pgvector connector",
1723
+ "vectorstorewriter"
1724
+ ],
1725
+ "anchors": {
1726
+ "naming-trap": "#a-pegadinha-de-nomenclatura-crítico--leia-antes-de-tudo",
1727
+ "maturity": "#maturidade-atual-verificado-2026-07-06",
1728
+ "decision-matrix": "#decision-matrix--custom-pgvector-vs-vectordata",
1729
+ "data-ingestion": "#microsoftextensionsdataingestion-preview--pipeline-pattern-para-adoção-futura"
1730
+ },
1731
+ "digest": "Microsoft.Extensions.VectorData.Abstractions (GA) is the provider-agnostic vector-store abstraction; Microsoft.Extensions.DataIngestion (preview) is the companion ETL pipeline. Concrete connectors (Postgres, Qdrant, SQL Server...) ship under Microsoft.SemanticKernel.Connectors.* package names despite having nothing to do with Semantic Kernel — an explicit exception to ai-agents-setup's SK ban. Default RAG storage stays ai-agents-rag-custom-pgvector; adopt VectorData only for genuine multi-backend portability, since the Postgres connector is still prerelease."
1660
1732
  }
1661
1733
  ],
1662
1734
  "sharedAliases": {