@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.
- package/README.md +2 -2
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/agents.json +1 -1
- package/framework/commands/morph-preflight.md +10 -5
- package/framework/rules/infrastructure-standards.md +4 -4
- package/framework/skills/core/create-umbraco-lp/SKILL.md +22 -2
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +84 -5
- package/framework/skills/core/morph-apply/SKILL.md +1 -1
- package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
- package/framework/skills/core/morph-eval/SKILL.md +1 -1
- package/framework/skills/core/morph-implement/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +1 -1
- package/framework/skills/core/morph-proposal/SKILL.md +1 -1
- package/framework/skills/core/morph-review/SKILL.md +1 -1
- package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/standards/STANDARDS.json +80 -8
- package/framework/standards/ai-agents/durable-workflows-hitl.md +191 -0
- package/framework/standards/ai-agents/rag-custom-pgvector.md +246 -243
- package/framework/standards/ai-agents/setup.md +3 -0
- package/framework/standards/ai-agents/vector-data-extensions.md +170 -0
- package/framework/standards/ai-agents/workflows.md +190 -187
- package/framework/standards/infrastructure/docker/{easypanel-deploy.md → coolify-deploy.md} +49 -20
- package/framework/templates/README.md +7 -7
- package/framework/templates/REGISTRY.json +14 -14
- package/framework/templates/context/CONTEXT.md +3 -3
- package/framework/templates/docs/proposal.md +2 -2
- package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +1 -1
- package/framework/templates/infrastructure/docker/coolify.template.json +49 -0
- package/framework/templates/infrastructure/github/README.md +6 -6
- package/framework/templates/infrastructure/github/workflows/deploy-coolify.yml.hbs +33 -0
- package/package.json +1 -1
- package/framework/templates/infrastructure/docker/easypanel.template.json +0 -46
- 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.
|
|
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.
|
|
368
|
+
*morph-spec v7.6.0 by [Polymorphism Tech](https://polymorphism.tech)*
|
package/claude-plugin.json
CHANGED
package/docs/CHEATSHEET.md
CHANGED
package/docs/QUICKSTART.md
CHANGED
package/framework/agents.json
CHANGED
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
"stack": ["dotnet"],
|
|
112
112
|
"standards": [
|
|
113
113
|
"backend-integrations-hangfire-hangfire-jobs",
|
|
114
|
-
"infrastructure-docker-
|
|
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
|
|
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/
|
|
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.
|
|
156
|
+
### 6. Coolify Configuration
|
|
157
157
|
|
|
158
158
|
**What it checks:**
|
|
159
|
-
-
|
|
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
|
-
|
|
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
|
-
- "**/
|
|
6
|
+
- "**/coolify*"
|
|
7
7
|
stacks:
|
|
8
8
|
- docker
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# Infrastructure Standards
|
|
12
12
|
|
|
13
|
-
@.morph/framework/standards/infrastructure/docker/
|
|
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
|
-
|
|
|
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
|
|
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.
|
|
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
|
|
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.
|
|
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
|
-
**
|
|
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
|
-
|
|
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 (
|
|
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.
|
|
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.
|
|
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.
|
|
7
|
+
cliVersion: "7.6.0"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# morph-eval
|
|
@@ -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.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.
|
|
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.
|
|
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.
|
|
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.
|
|
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-
|
|
1499
|
-
"name": "
|
|
1500
|
-
"path": "infrastructure/docker/
|
|
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
|
-
"
|
|
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": "#
|
|
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": "
|
|
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": {
|