@polymorphism-tech/morph-spec 9.4.0 → 9.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 (106) hide show
  1. package/CLAUDE.md +1 -1
  2. package/README.md +2 -2
  3. package/bin/morph-spec.js +1 -0
  4. package/claude-plugin.json +1 -1
  5. package/docs/CHEATSHEET.md +1 -1
  6. package/docs/QUICKSTART.md +1 -1
  7. package/framework/MORPH.md +23 -33
  8. package/framework/agents.json +6 -7
  9. package/framework/docs/CLI.md +12 -8
  10. package/framework/hooks/README.md +2 -2
  11. package/framework/hooks/claude-code/__pycache__/statusline.cpython-313.pyc +0 -0
  12. package/framework/hooks/claude-code/core/comment-guard.js +1 -0
  13. package/framework/hooks/claude-code/core/denylist.json +63 -1
  14. package/framework/hooks/claude-code/core/enforce-phase-writes.js +0 -8
  15. package/framework/hooks/claude-code/core/gate-guard.js +0 -8
  16. package/framework/hooks/claude-code/core/heavy-gate.js +5 -3
  17. package/framework/hooks/claude-code/core/loop-detect.js +0 -3
  18. package/framework/hooks/claude-code/core/pre-bash-denylist.js +2 -1
  19. package/framework/hooks/claude-code/core/protect-spec-files.js +0 -7
  20. package/framework/hooks/claude-code/core/validate-completion.js +0 -4
  21. package/framework/hooks/claude-code/core/worktree-context.js +0 -2
  22. package/framework/hooks/claude-code/statusline.py +3 -44
  23. package/framework/hooks/shared/build-check.js +12 -19
  24. package/framework/hooks/shared/comment-rules.js +11 -1
  25. package/framework/hooks/shared/feature-locator.js +7 -5
  26. package/framework/hooks/shared/heavy-command.js +12 -0
  27. package/framework/hooks/shared/heavy-config.js +28 -1
  28. package/framework/hooks/shared/heavy-slots.js +41 -5
  29. package/framework/hooks/shared/hook-response.js +0 -4
  30. package/framework/hooks/shared/payload-utils.js +1 -35
  31. package/framework/hooks/shared/phase-utils.js +0 -12
  32. package/framework/hooks/shared/slice-progress.js +0 -2
  33. package/framework/hooks/shared/state-reader.js +0 -19
  34. package/framework/hooks/shared/telemetry-logger.js +1 -1
  35. package/framework/rules/frontend-standards.md +1 -2
  36. package/framework/rules/nextjs-standards.md +9 -8
  37. package/framework/schemas/config.schema.json +12 -1
  38. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  39. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  40. package/framework/skills/core/morph-apply/SKILL.md +1 -1
  41. package/framework/skills/core/morph-hotfix/SKILL.md +1 -1
  42. package/framework/skills/core/morph-machine/SKILL.md +2 -2
  43. package/framework/skills/core/morph-media/SKILL.md +1 -1
  44. package/framework/skills/core/morph-plan/SKILL.md +2 -2
  45. package/framework/skills/core/morph-review/SKILL.md +1 -1
  46. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  47. package/framework/skills/core/morph-uiux/SKILL.md +1 -1
  48. package/framework/standards/STANDARDS.json +32 -29
  49. package/framework/standards/STANDARDS.meta.json +13 -15
  50. package/framework/standards/architecture/code-comments.md +1 -1
  51. package/framework/standards/frontend/admin-app/data-table.md +1 -3
  52. package/framework/standards/frontend/admin-app/page-structure.md +8 -4
  53. package/framework/standards/frontend/nextjs/app-router.md +3 -4
  54. package/framework/standards/frontend/nextjs/components.md +2 -15
  55. package/framework/standards/frontend/nextjs/data-fetching.md +2 -3
  56. package/framework/standards/frontend/nextjs/lint.md +141 -0
  57. package/framework/standards/frontend/nextjs/motion-patterns.md +2 -7
  58. package/framework/standards/frontend/nextjs/naming-conventions.md +1 -2
  59. package/framework/standards/frontend/nextjs/nextjs-patterns.md +5 -7
  60. package/framework/standards/frontend/nextjs/project-structure.md +3 -2
  61. package/framework/standards/frontend/nextjs/state-management.md +6 -7
  62. package/framework/standards/infrastructure/docker/local-compose-isolation.md +1 -0
  63. package/framework/templates/README.md +4 -4
  64. package/framework/templates/REGISTRY.json +21 -309
  65. package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +54 -56
  66. package/package.json +3 -2
  67. package/src/commands/e2e/e2e.js +3 -0
  68. package/src/commands/project/env.js +3 -3
  69. package/src/commands/project/heavy.js +10 -3
  70. package/src/commands/project/worktree.js +2 -1
  71. package/src/commands/state/finish.js +17 -3
  72. package/src/commands/validation/mutate.js +2 -2
  73. package/src/commands/validation/verify.js +3 -0
  74. package/src/core/paths/output-schema.js +0 -9
  75. package/src/core/state/feature-identity.js +0 -2
  76. package/src/core/state/state-manager.js +1 -169
  77. package/src/core/templates/template-renderer.js +0 -23
  78. package/src/dashboard/app/lib/format.js +0 -7
  79. package/src/dashboard/app/lib/phases.js +0 -7
  80. package/src/dashboard/scanner/activity-scanner.js +0 -4
  81. package/src/lib/ai/ai-project-scan.js +1 -6
  82. package/src/lib/e2e/compose-env.js +26 -3
  83. package/src/lib/graph/graphify-runtime.js +0 -1
  84. package/src/lib/heavy/gate.js +23 -4
  85. package/src/lib/heavy/live-stacks.js +64 -0
  86. package/src/lib/mutation/snapshot.js +1 -5
  87. package/src/lib/risk-detector.js +9 -6
  88. package/src/lib/stack/stack-profile.js +0 -4
  89. package/src/lib/stack/stack-scan.js +15 -4
  90. package/src/lib/tasks/eval-node.js +1 -7
  91. package/src/lib/validators/validation-runner.js +0 -43
  92. package/src/lib/validators/verify-runner.js +18 -2
  93. package/src/lib/worktree/docker-audit.js +7 -3
  94. package/src/lib/worktree/env-allocator.js +27 -5
  95. package/src/lib/worktree/ignored-triage.js +8 -0
  96. package/src/utils/agents-installer.js +12 -19
  97. package/src/utils/claude-settings-manager.js +0 -28
  98. package/src/utils/file-copier.js +0 -53
  99. package/framework/hooks/shared/activity-logger.js +0 -131
  100. package/framework/standards/frontend/design-system/naming.md +0 -68
  101. package/src/commands/validation/index.js +0 -2
  102. package/src/lib/validators/code-budget/index.js +0 -2
  103. package/src/lib/validators/contracts/index.js +0 -1
  104. package/src/lib/validators/nextjs/index.js +0 -1
  105. package/src/lib/validators/packages/index.js +0 -1
  106. package/src/lib/validators/reuse/index.js +0 -2
@@ -2,8 +2,6 @@
2
2
  // Source of truth: src/core/paths/output-schema.js
3
3
  // Regenerate with: node scripts/generate-refs.js
4
4
 
5
- export const PHASE_ORDER = ["plan","uiux","implement","review"];
6
-
7
5
  export const PHASE_DIRS = {
8
6
  plan: '2-plan',
9
7
  uiux: '1-design',
@@ -63,16 +61,6 @@ export function isFeaturePath(filePath) {
63
61
  return normalized.includes('.morph/features/');
64
62
  }
65
63
 
66
- export function isFrameworkPath(filePath) {
67
- const normalized = filePath.replace(/\\/g, '/');
68
- return normalized.includes('.morph/framework/');
69
- }
70
-
71
- export function isStatePath(filePath) {
72
- const normalized = filePath.replace(/\\/g, '/');
73
- return normalized.endsWith('.morph/state.json');
74
- }
75
-
76
64
  export function getBasename(filePath) {
77
65
  const normalized = filePath.replace(/\\/g, '/');
78
66
  return normalized.split('/').pop();
@@ -7,8 +7,6 @@ export const PARTIAL_MARKER = /^[\s#>*_]*PARCIAL\b/;
7
7
  /** `BLOQUEADO`: the slice waits on a decision only the human can make; re-dispatching it repeats the question. */
8
8
  export const BLOCKED_MARKER = /^[\s#>*_]*BLOQUEADO\b/;
9
9
 
10
- export const SLICE_STATUSES = ['pendente', 'parcial', 'bloqueado', 'feito'];
11
-
12
10
  export function checksPath(featureDir) {
13
11
  return join(featureDir, '2-plan', 'checks.json');
14
12
  }
@@ -185,11 +185,6 @@ export function derivePhaseFrom(feature, hasArtifact) {
185
185
  return PHASE_INDEX[implied] > PHASE_INDEX[artifactPhase] ? implied : artifactPhase;
186
186
  }
187
187
 
188
- export function gateImpliedPhase(featureName, projectPath) {
189
- const designDir = join(projectPath || process.cwd(), '.morph/features', featureName, DESIGN_DIR);
190
- return gateImpliedPhaseFrom(getFeature(featureName, projectPath), existsSync(designDir));
191
- }
192
-
193
188
  export function derivePhaseForFeature(featureName, projectPath) {
194
189
  const basePath = join(projectPath || process.cwd(), '.morph/features', featureName);
195
190
  return derivePhaseFrom(getFeature(featureName, projectPath), diskArtifactProbe(basePath));
@@ -219,17 +214,3 @@ export function pendingGatesFrom(feature) {
219
214
  export function getPendingGates(featureName, projectPath) {
220
215
  return pendingGatesFrom(getFeature(featureName, projectPath));
221
216
  }
222
-
223
- export function getOutputs(featureName, projectPath) {
224
- const feature = getFeature(featureName, projectPath);
225
- return feature?.outputs || null;
226
- }
227
-
228
- export function getMissingOutputs(featureName, projectPath) {
229
- const feature = getFeature(featureName, projectPath);
230
- if (!feature?.outputs) return [];
231
-
232
- return Object.entries(feature.outputs)
233
- .filter(([, output]) => !output.created)
234
- .map(([type, output]) => ({ type, path: output.path }));
235
- }
@@ -70,7 +70,7 @@ export function readTelemetryEvents(projectPath) {
70
70
  }
71
71
  }
72
72
 
73
- // `events-` prefix: activity-logger archives share `sessions/` without one.
73
+ // `events-` prefix: older activity-log archives in `sessions/` carry none.
74
74
  function archiveEvents(projectPath) {
75
75
  try {
76
76
  const eventsPath = getEventsPath(projectPath);
@@ -9,8 +9,7 @@ stacks:
9
9
 
10
10
  # Frontend Standards
11
11
 
12
- - **frontend-design-system-naming** (`frontend/design-system/naming.md`) — BEM para componente reutilizável (`.card`, `.card__header`, `.card--featured`); prefixo de página para elemento específico (`.home-hero`). Custom properties em `:root` com namespace `--app-`. Estado por sufixo fixo: `--active`, `--disabled`, `--loading`, `--error`.
13
- - **frontend-design-system-animations** (`frontend/design-system/animations.md`) — Classes `.animate-*`, `.duration-fast/normal/slow`, `.stagger-1..10`. Lista escalonada exige `animation-delay` E `animation-fill-mode: backwards`. Sempre o bloco `prefers-reduced-motion: reduce`. Além do CSS: Motion (`motion/react`) para enter/exit/layout; GSAP só para ScrollTrigger, SVG morph e timelines.
12
+ - **frontend-design-system-animations** (`frontend/design-system/animations.md`) — Lista escalonada exige `animation-delay` E `animation-fill-mode: backwards` (senão pisca antes do atraso). Sempre o bloco `prefers-reduced-motion: reduce`. Além do CSS: Motion (`motion/react`) para enter/exit/layout; GSAP só para ScrollTrigger, SVG morph e timelines.
14
13
  - **frontend-design-system-art-direction** (`frontend/design-system/art-direction.md`) — Tela nova ou alterada segue a direção escrita em `1-design/direcao.md` e o protótipo de `1-design/prototipos/`: celular composto (folha inferior, alvos de 44px), movimento só em `transform`/`opacity` com versão estática em `prefers-reduced-motion`, nenhum número inventado em cena ilustrativa.
15
14
 
16
15
  > Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
@@ -8,16 +8,17 @@ stacks:
8
8
 
9
9
  # Next.js Standards
10
10
 
11
- - **frontend-nextjs-naming-conventions** (`frontend/nextjs/naming-conventions.md`) — Arquivo SEMPRE kebab-case (`user-card.tsx`); export PascalCase para componente e `useX` para hook. Zod em `.schemas.ts`, tipos em `.types.ts` via `z.infer`. Named export (só `page.tsx` usa `default`). Nunca misture casos na mesma categoria.
12
- - **frontend-nextjs-project-structure** (`frontend/nextjs/project-structure.md`) — `src/app/` só roteia e importa de `features/`. Cada `src/features/{f}/` tem `components/`, `hooks/`, `types/` e `index.ts` como API pública (import de `@/features/users`, nunca deep path). NUNCA uma feature importa outra; `components/ui/` é shadcn e não se edita.
13
- - **frontend-nextjs-app-router** (`frontend/nextjs/app-router.md`) — Server Component por padrão; `'use client'` só para interação, browser API ou TanStack Query. Nunca fetch em `useEffect`. `loading.tsx`/`error.tsx` ao lado do `page.tsx` (`error.tsx` é client). Server Component chama o .NET com `BACKEND_API_PREFIX`; evite `route.ts`.
14
- - **frontend-nextjs-components** (`frontend/nextjs/components.md`) — `components/ui/` é saída do shadcn CLI (nunca editar; envolva em `components/`); `components/` sem regra de negócio e sem importar `features/`; `features/*/components/` conhece o domínio. Props tipadas e validadas com Zod, sempre com `className` opcional.
15
- - **frontend-nextjs-data-fetching** (`frontend/nextjs/data-fetching.md`) — Server Component com `BACKEND_API_PREFIX`; Client Component só pelo proxy same-origin com `PROXY_API_PREFIX`. TanStack Query 5 com key factory por feature e `invalidateQueries({ queryKey })`; toda resposta passa por Zod. PROIBIDO: `useEffect(() => fetch…)` e paginar/filtrar a coleção inteira no browser.
11
+ - **frontend-nextjs-lint** (`frontend/nextjs/lint.md`) — O lint cobra o mecânico: `framer-motion`, Zustand/Redux, driver de banco, Form legado, deep import de feature, fronteiras `components`/`features`/`app`/`hooks`, fetch em `useEffect`, `'use client'` no layout raiz, `export default` fora de rota, `any`. O `verify` roda o eslint nos arquivos da feature quando há `eslint.config.*`; sem `eslint.config.mjs`: `morph-spec standards --show frontend-nextjs-lint#files`. Regra mecânica nova vira linha nele, nunca prosa.
12
+ - **frontend-nextjs-naming-conventions** (`frontend/nextjs/naming-conventions.md`) — Arquivo SEMPRE kebab-case (`user-card.tsx`); componente PascalCase. Zod em `.schemas.ts`, tipos em `.types.ts` via `z.infer`. Nunca misture casos na mesma categoria.
13
+ - **frontend-nextjs-project-structure** (`frontend/nextjs/project-structure.md`) — `src/app/` só roteia; a lógica da tela mora em `src/features/{f}/` (`components/`, `hooks/`, `types/`, `index.ts` como API pública). NUNCA uma feature importa outra (extraia para `components/` ou `lib/`); `components/ui/` é shadcn e não se edita.
14
+ - **frontend-nextjs-app-router** (`frontend/nextjs/app-router.md`) — Server Component por padrão; `'use client'` só para interação, browser API ou TanStack Query. `loading.tsx`/`error.tsx` ao lado do `page.tsx`. Server Component chama o .NET com `BACKEND_API_PREFIX`; evite `route.ts`.
15
+ - **frontend-nextjs-components** (`frontend/nextjs/components.md`) — `components/ui/` é saída do shadcn CLI (nunca editar; envolva em `components/`); `components/` sem regra de negócio; `features/*/components/` conhece o domínio e entrega props tipadas, nunca a resposta crua da API.
16
+ - **frontend-nextjs-data-fetching** (`frontend/nextjs/data-fetching.md`) — Server Component com `BACKEND_API_PREFIX`; Client Component só pelo proxy same-origin com `PROXY_API_PREFIX`. TanStack Query 5 com key factory por feature e `invalidateQueries({ queryKey })`; toda resposta passa por Zod. PROIBIDO paginar/filtrar a coleção inteira no browser.
16
17
  - **frontend-nextjs-forms** (`frontend/nextjs/forms.md`) — react-hook-form + Zod (`zodResolver`) + Field do shadcn via `Controller`; tipo por `z.infer`; nunca `useState` por campo. Submit por `useMutation`, erro em `root`. Edição reusa o schema de criação (`.partial()`). Zod 4: `z.email()`.
17
- - **frontend-nextjs-state-management** (`frontend/nextjs/state-management.md`) — Estado de servidor no TanStack Query, nunca copiado para `useState`. Página, ordenação, filtros e aba ativa na URL (`searchParams`). Context só para tema, usuário ou locale. NUNCA Zustand/Redux antes de esgotar Server Components + TanStack Query.
18
+ - **frontend-nextjs-state-management** (`frontend/nextjs/state-management.md`) — Estado de servidor no TanStack Query, nunca copiado para `useState`. Página, ordenação, filtros e aba ativa na URL (`searchParams`). Context só para tema, usuário ou locale.
18
19
  - **frontend-nextjs-testing** (`frontend/nextjs/testing.md`) — Vitest + Testing Library; MSW para API; Playwright para e2e. Teste comportamento, não implementação. Orçamento por risco: um teste por cenário com `risco:` + o de regressão do bug, componente sem risco é `leitura`/`e2e`; cenários do mesmo comportamento em `it.each`; nada de teste que só confere string de copy.
19
- - **frontend-nextjs-nextjs-patterns** (`frontend/nextjs/nextjs-patterns.md`) — Route Handler é BFF: `auth.getSession()` (401 sem sessão) e delega ao .NET; banco NUNCA em route handler. Zod no client e no server (`safeParse`, 400). `strict: true` + `noUncheckedIndexedAccess`; sem `any` em resposta de API. shadcn via `npx shadcn@latest add`.
20
- - **frontend-nextjs-motion-patterns** (`frontend/nextjs/motion-patterns.md`) — Motion via `motion/react` (nunca `framer-motion` junto). Variants em `motion/variants.ts` com durations/easings de `@/lib/design-tokens`. `useReducedMotion()` obrigatório; o `layout.tsx` raiz segue Server Component. 3–5 momentos de motion por página; cena guiada por scroll é do GSAP, nunca no mesmo elemento.
20
+ - **frontend-nextjs-nextjs-patterns** (`frontend/nextjs/nextjs-patterns.md`) — Route Handler é BFF: `auth.getSession()` (401 sem sessão) e delega ao .NET. Zod no client e no server (`safeParse`, 400). `strict: true` + `noUncheckedIndexedAccess`. shadcn via `npx shadcn@latest add`.
21
+ - **frontend-nextjs-motion-patterns** (`frontend/nextjs/motion-patterns.md`) — Motion via `motion/react`. Variants em `motion/variants.ts` com durations/easings de `@/lib/design-tokens`. `useReducedMotion()` obrigatório. Cena guiada por scroll é do GSAP, nunca no mesmo elemento que o Motion.
21
22
  - **architecture-code-comments** (`architecture/code-comments.md`) — Padrão é nenhum comentário: renomeie, extraia, tipe ou teste antes. Só o porquê que o código não mostra (invariante, ordem, quirk externo com link), até ~3 linhas. PROIBIDO: história de processo (task, UC, ADR, Gate), repetir o código, XML doc/JSDoc que repete a assinatura, banner, `#region`, código comentado, TODO solto. O hook `comment-guard` bloqueia.
22
23
  - **architecture-engineering-lens** (`architecture/engineering-lens.md`) — Escreva para quem herda amanhã: convenção do projeto, o mais simples, causa raiz. PROIBIDO no que você adiciona: `catch` que engole, flag booleana que bifurca o método (são dois métodos), efeito colateral escondido, símbolo/flag/config sem consumidor, abstração ou camada sem segundo uso, literal repetido sem constante. Nomes de domínio buscáveis, funções pequenas, ≤ 3 parâmetros, condicional positiva. Commit em Conventional Commits (hook `conventional-commit`).
23
24
 
@@ -179,7 +179,18 @@
179
179
  "type": "number",
180
180
  "minimum": 0,
181
181
  "default": 4,
182
- "description": "Free RAM (os.freemem) below which a heavy node WAITS instead of starting and dying 'killed' for memory. It waits with or without a slot holder - the pressure often comes from outside (WSL vmmem, the IDE). 0 disables the check."
182
+ "description": "Free RAM (os.freemem) below which a heavy node WAITS instead of starting and dying 'killed' for memory. It waits with or without a slot holder - the pressure often comes from outside (WSL vmmem, the IDE). 0 disables the check. Since the per-kind floors exist it is the fallback: set explicitly, it applies to every kind not listed in minFreeMemGbByKind (so 0 still turns the check off everywhere)."
183
+ },
184
+ "minFreeMemGbByKind": {
185
+ "type": "object",
186
+ "additionalProperties": false,
187
+ "default": { "unit": 1.5, "build": 4, "e2e": 4 },
188
+ "description": "Free-RAM floor per kind of heavy command. unit = vitest/jest, npm|pnpm|yarn test, the verify tests node; e2e = playwright, docker compose up, the verify e2e node, e2e up; build = everything else (dotnet build|test|publish, next build, docker build, mutate, unknown commands). A verify admitted at a lighter node waits again, holding its slot, before a heavier one. A kind left out takes an explicit heavy.minFreeMemGb, else its default.",
189
+ "properties": {
190
+ "unit": { "type": "number", "minimum": 0 },
191
+ "build": { "type": "number", "minimum": 0 },
192
+ "e2e": { "type": "number", "minimum": 0 }
193
+ }
183
194
  },
184
195
  "waitTimeoutSec": {
185
196
  "type": "number",
@@ -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_use: Use quando for iniciar um novo projeto de LP com o template Umbraco genérico
6
6
  user-invocable: true
7
- cliVersion: "9.4.0"
7
+ cliVersion: "9.6.0"
8
8
  stacks:
9
9
  - umbraco
10
10
  ---
@@ -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_use: Use quando o usuário pedir "use /migrate-lp-to-umbraco {projeto}" ou descrever a necessidade de migrar uma LP para Umbraco
6
6
  user-invocable: true
7
- cliVersion: "9.4.0"
7
+ cliVersion: "9.6.0"
8
8
  stacks:
9
9
  - umbraco
10
10
  ---
@@ -4,7 +4,7 @@ description: Executa as fatias do plano de uma feature morph-spec — dispatch p
4
4
  version: core
5
5
  when_to_use: Use com o gate do plano aprovado, em "use /morph-apply {feature}" ou "implementar feature"
6
6
  user-invocable: true
7
- cliVersion: "9.4.0"
7
+ cliVersion: "9.6.0"
8
8
  ---
9
9
 
10
10
  # morph-apply
@@ -4,7 +4,7 @@ description: ADW cirúrgico de hotfix — resolve uma queda/bug crítico de prod
4
4
  version: core
5
5
  when_to_use: Use quando chegar "/morph-hotfix {feature}", quando a morph-plan rotear o pedido como workType hotfix, ou quando o usuário disser "produção caiu / hotfix / urgente" a respeito de um incidente
6
6
  user-invocable: true
7
- cliVersion: "9.4.0"
7
+ cliVersion: "9.6.0"
8
8
  ---
9
9
 
10
10
  # morph-hotfix
@@ -4,7 +4,7 @@ description: Cuida da RAM e da CPU da máquina enquanto o morph-spec trabalha
4
4
  version: core
5
5
  when_to_use: Use quando a máquina estiver lenta ou sem RAM, um comando pesado esperou ou foi recusado pela fila, sobrou worktree de sub-agent, ou vários sub-agents pesados vão rodar em paralelo
6
6
  user-invocable: false
7
- cliVersion: "9.4.0"
7
+ cliVersion: "9.6.0"
8
8
  ---
9
9
 
10
10
  # morph-machine
@@ -43,4 +43,4 @@ Limites: não cobre `run_in_background`, PowerShell nem comando fora da lista; u
43
43
  ## Referências
44
44
 
45
45
  - `.morph/framework/CLI.md` — `### heavy`, `### worktree reap`, `### doctor --procs` e `### worktree.root`.
46
- - `MORPH.md` §2.2 (worktrees) e a cláusula 6 dos templates de dispatch (PROCESSOS).
46
+ - `MORPH.md` §2.2 (worktrees) e a cláusula PROCESSOS do contrato de dispatch.
@@ -4,7 +4,7 @@ description: Conduz a AUTORIA de mídia gerada por IA — escolher o modelo pelo
4
4
  version: core
5
5
  when_to_use: Use quando a tarefa for PRODUZIR imagem ou vídeo por IA (catálogo, marketing, asset de LP) e alguém precisar decidir modelo, prompt e aprovação
6
6
  user-invocable: true
7
- cliVersion: "9.4.0"
7
+ cliVersion: "9.6.0"
8
8
  stacks:
9
9
  - dotnet
10
10
  - nextjs
@@ -4,7 +4,7 @@ description: Leva uma feature do pedido ao plano aprovado — roteia o tipo de t
4
4
  version: core
5
5
  when_to_use: Use quando uma feature, bug ou chore começar ("/morph-plan {feature}", "vamos planejar X"), ou ao retomar uma feature cujo gate plan ainda não foi aprovado
6
6
  user-invocable: true
7
- cliVersion: "9.4.0"
7
+ cliVersion: "9.6.0"
8
8
  ---
9
9
 
10
10
  # morph-plan
@@ -71,7 +71,7 @@ O `create` já escreveu o esqueleto em `2-plan/plan.md` (leia e preencha; `chore
71
71
  - **Observável**: que asserção (ou que linha de código) prova? Sem resposta, reescreva ou descarte.
72
72
  - **Artefato não é comportamento.** Cenário que cita `grep`, "contém a string", "o binário", "nenhum arquivo", "arquivo/pasta/diretório em" ou glob `**/` descreve o repositório. Tente o efeito em runtime; se o artefato É o entregável, justifique em `> Por que artefato:` logo abaixo (nunca na mesma linha, nunca `> [!note]`).
73
73
  - **Saída de LLM** (o que o modelo responde) prova-se por `eval` de runtime, nunca pelo texto do prompt: evidência em `3-implement/evidencias/{Cn}.md`, path listado em `arquivos:` da fatia; com nó `runner: "evals"` (standard `ai-agents-evals-with-cache`) o cenário mora no dataset. No máximo 1 teste de estrutura da fonte do prompt.
74
- - **"O comando de teste da raiz roda de ponta a ponta"** prova-se pelo `verify {feature}` (o nó `e2e` e o nó de teste `e2e: true` já percorrem a cadeia): nomeie essa rodada como a prova, nunca uma execução avulsa. Precisando mesmo rodar fora dele, numa linha só (o Bash não guarda o env entre chamadas): `eval "$(morph-spec env {feature})" && morph-spec heavy run -- <comando>` — o `COMPOSE_PROJECT_NAME` da feature é o que o `finish` derruba com os volumes. `prova: manual` registra passo e resultado em `3-implement/evidencias/{Cn}.md`.
74
+ - **"O comando de teste da raiz roda de ponta a ponta"** prova-se pelo `verify {feature}` (o nó `e2e` e o nó de teste `e2e: true` já percorrem a cadeia): nomeie essa rodada como a prova, nunca uma execução avulsa. Precisando mesmo rodar fora dele, numa linha só (o Bash não guarda o env entre chamadas): `eval "$(morph-spec env {feature} --with-secrets)" && morph-spec heavy run -- <comando>` — o `COMPOSE_PROJECT_NAME` da feature é o que o `finish` derruba com os volumes. `prova: manual` registra passo e resultado em `3-implement/evidencias/{Cn}.md`.
75
75
  - **Orçamento de teste**: um teste por cenário de risco — o cenário é a unidade, nunca a pós-condição nem o componente; variantes são linhas de tabela (`[Theory]`/`it.each`); acima disso, o porquê na linha do cenário. Nenhum teste que só fixa texto, prompt ou constante, nenhum de DTO/mapping, e o mesmo comportamento numa camada só (handler **ou** endpoint). Teste além do orçamento é escopo para o revisor. O `verify` avisa quando o teste passa da produção.
76
76
 
77
77
  ```markdown
@@ -4,7 +4,7 @@ description: Conduz o Gate 3 — despacha verificador ∥ revisor (∥ seguranç
4
4
  version: core
5
5
  when_to_use: Use quando a fase review começar, com todas as fatias com relatório final e o verify da feature verde
6
6
  user-invocable: true
7
- cliVersion: "9.4.0"
7
+ cliVersion: "9.6.0"
8
8
  ---
9
9
 
10
10
  # morph-review
@@ -4,7 +4,7 @@ description: Localiza um standard específico via busca server-side (morph-spec
4
4
  version: core
5
5
  when_to_use: Use quando precisar consultar um standard específico durante a implementação
6
6
  user-invocable: true
7
- cliVersion: "9.4.0"
7
+ cliVersion: "9.6.0"
8
8
  ---
9
9
 
10
10
  # morph-standards
@@ -4,7 +4,7 @@ description: Conduz a fase opcional de UI/UX de uma feature com plano aprovado
4
4
  version: core
5
5
  when_to_use: Use quando o ramo de design for tomado (morph-spec advance {feature} --design, depois do gate do plano) e antes de /morph-apply
6
6
  user-invocable: true
7
- cliVersion: "9.4.0"
7
+ cliVersion: "9.6.0"
8
8
  ---
9
9
 
10
10
  # morph-uiux
@@ -1211,26 +1211,7 @@
1211
1211
  "tokens": "#stagger--timing",
1212
1212
  "principles": "#css-animation-patterns"
1213
1213
  },
1214
- "digest": "CSS animation library for design-system.css: keyframes (fadeIn/Out, slideIn Up/Down/Left/Right, scaleIn, bounce, pulse, spin, shimmer) exposed as `.animate-*` classes (0.3s fade/scale, 0.5s slides; bounce 1s, pulse 2s, spin 1s infinite) plus `.duration-fast/normal/slow` (150/300/500ms), `.stagger-1..10` (0.1s steps) and `.ease-bounce`. For staggered lists set `animation-delay` AND `animation-fill-mode: backwards` (marked CRITICAL: otherwise elements flash before the delay). Skeletons use `.shimmer` (1.5s infinite). Hover/focus transitions run 0.15s to 0.3s. Always include the `@media (prefers-reduced-motion: reduce)` block (0.01ms durations, one iteration). Beyond CSS: Motion (formerly Framer Motion) for simple enter/exit, layout and gesture transitions (`frontend/nextjs/motion-patterns.md`); GSAP for ScrollTrigger, SVG morph, timelines (`frontend/gsap/gsap-core.md`)."
1215
- },
1216
- {
1217
- "id": "frontend-design-system-naming",
1218
- "name": "naming",
1219
- "path": "frontend/design-system/naming.md",
1220
- "category": "frontend",
1221
- "subcategory": "design-system",
1222
- "tags": [
1223
- "frontend",
1224
- "design-system"
1225
- ],
1226
- "stacks": [
1227
- "*"
1228
- ],
1229
- "anchors": {
1230
- "blocks": "#bem-for-reusable-components",
1231
- "tokens": "#css-variables-with-namespace"
1232
- },
1233
- "digest": "CSS naming rests on four principles: a page prefix prevents conflicts, BEM for components gives clear hierarchy, namespaced variables avoid library collisions, and state suffixes keep modifiers consistent. Page-specific elements take the page name as prefix (.home-hero, .checkout-form, .dashboard-stats, .auth-form); reusable components use BEM: block .card, element .card__header, modifier .card--featured. Rule: BEM for reusable components, page prefix for page-specific elements. Custom properties go in :root under the --app- namespace (--app-primary, --app-spacing-md, --app-font-heading). State modifiers use fixed suffixes: --active, --disabled, --loading, --error (.button, .button--active, .button--disabled)."
1214
+ "digest": "CSS animation library for design-system.css (keyframes and the `.animate-*`/`.stagger-*` utility classes live in the file, not in a rule). For staggered lists set `animation-delay` AND `animation-fill-mode: backwards` (marked CRITICAL: otherwise elements flash before the delay). Always include the `@media (prefers-reduced-motion: reduce)` block (0.01ms durations, one iteration). Beyond CSS: Motion (formerly Framer Motion) for simple enter/exit, layout and gesture transitions (`frontend/nextjs/motion-patterns.md`); GSAP for ScrollTrigger, SVG morph, timelines (`frontend/gsap/gsap-core.md`)."
1234
1215
  },
1235
1216
  {
1236
1217
  "id": "frontend-design-system-premium-finish",
@@ -1341,7 +1322,7 @@
1341
1322
  "layout": "#root-layout--required-providers",
1342
1323
  "routing": "#app-directory-structure"
1343
1324
  },
1344
- "digest": "Next.js 15 App Router, TypeScript only (no .js/.jsx). Server Components by default; add `'use client'` only for interaction (onClick, useState/useEffect/useRef), browser APIs, TanStack Query hooks and interactive shadcn/ui. `app/` is for routing only: business logic goes to `features/`, never in `page.tsx`. Never fetch data in a Client Component when a Server Component can, and never fetch in `useEffect`: use a Server Component or TanStack Query. Co-locate `loading.tsx` and `error.tsx` (`error.tsx` must be a Client Component) beside `page.tsx`. Server Components call the .NET backend directly using the dedicated `BACKEND_API_PREFIX` from `@/lib/api/prefixes`, never the same-origin proxy prefix used by Client Components; always check `res.ok`. Avoid `route.ts` API routes (use the .NET API). The root `layout.tsx` wraps children in `QueryProvider` and the sonner `Toaster`."
1325
+ "digest": "Next.js 15 App Router, TypeScript only (no .js/.jsx). Server Components by default; add `'use client'` only for interaction (onClick, useState/useEffect/useRef), browser APIs, TanStack Query hooks and interactive shadcn/ui. `app/` is for routing only: business logic goes to `features/`, never in `page.tsx`. Never fetch data in a Client Component when a Server Component can,. Co-locate `loading.tsx` and `error.tsx` beside `page.tsx`. Server Components call the .NET backend directly using the dedicated `BACKEND_API_PREFIX` from `@/lib/api/prefixes`, never the same-origin proxy prefix used by Client Components; always check `res.ok`. Avoid `route.ts` API routes (use the .NET API). The root `layout.tsx` wraps children in `QueryProvider` and the sonner `Toaster`. Fetch in `useEffect` and `'use client'` in the root layout are lint errors (frontend-nextjs-lint)."
1345
1326
  },
1346
1327
  {
1347
1328
  "id": "frontend-nextjs-components",
@@ -1360,7 +1341,7 @@
1360
1341
  "patterns": "#three-tier-hierarchy",
1361
1342
  "shadcn": "#tier-1--shadcnui-primitives"
1362
1343
  },
1363
- "digest": "Three tiers (Next.js 16 + shadcn 4 CLI): `components/ui/` is shadcn CLI output (add via `npx shadcn@latest add`, NEVER edit because the CLI regenerates it; wrap it in `components/` instead); `components/` holds shared composed components with no business logic and NEVER imports from `features/`; `features/*/components/` is feature-scoped and may know the domain. Add `'use client'` only if the component uses hooks, events or browser APIs (not on every component: unnecessary client JS). Never pass raw API/fetch data as props: transform it into typed props and validate with Zod first. Props interfaces always accept an optional `className` for Tailwind override. The shared data table is specified once in `admin-app/data-table.md`: do not write a second `components/data-table.tsx`; a feature list is a Tier 3 component that owns the columns and the URL-to-table mapping."
1344
+ "digest": "Three tiers (Next.js 16 + shadcn 4 CLI): `components/ui/` is shadcn CLI output (add via `npx shadcn@latest add`, NEVER edit because the CLI regenerates it; wrap it in `components/` instead); `components/` holds shared composed components with no business logic (importing `features/` is a lint error, frontend-nextjs-lint); `features/*/components/` is feature-scoped and may know the domain. Add `'use client'` only if the component uses hooks, events or browser APIs (not on every component: unnecessary client JS). Never pass raw API/fetch data as props: the feature maps it to typed props. The shared data table is specified once in `admin-app/data-table.md`: do not write a second `components/data-table.tsx`; a feature list is a Tier 3 component that owns the columns and the URL-to-table mapping."
1364
1345
  },
1365
1346
  {
1366
1347
  "id": "frontend-nextjs-data-fetching",
@@ -1379,7 +1360,7 @@
1379
1360
  "server": "#server-component--tanstack-query-handoff",
1380
1361
  "client": "#usequery-hook-pattern"
1381
1362
  },
1382
- "digest": "Server Components do the initial load and call the .NET backend directly with BACKEND_API_PREFIX; Client Components never call the backend — they hit a same-origin proxy route with PROXY_API_PREFIX. Two named constants, never one shared API_PREFIX: the side that drifts breaks silently at runtime. Client data is TanStack Query 5: useQuery for reads, useMutation for writes, then invalidateQueries({ queryKey: userKeys.lists() }) (object syntax). Keys come from a per-feature key factory, never hardcoded ['users']. Every response is parsed with a Zod schema. QueryClient: staleTime 60s, retry 1, created once in useState; the Server Component passes initialData to the client hook. FORBIDDEN: useEffect(() => fetch…, []) and fetching a whole collection to page/sort/filter in the browser — page on the server, query in the URL."
1363
+ "digest": "Server Components do the initial load and call the .NET backend directly with BACKEND_API_PREFIX; Client Components never call the backend — they hit a same-origin proxy route with PROXY_API_PREFIX. Two named constants, never one shared API_PREFIX: the side that drifts breaks silently at runtime. Client data is TanStack Query 5: useQuery for reads, useMutation for writes, then invalidateQueries({ queryKey: userKeys.lists() }) (object syntax). Keys come from a per-feature key factory, never hardcoded ['users']. Every response is parsed with a Zod schema. QueryClient is created once in useState; the Server Component passes initialData to the client hook. FORBIDDEN: fetching a whole collection to page/sort/filter in the browser — page on the server, query in the URL. Fetch in useEffect and a DB driver in Next are lint errors (frontend-nextjs-lint)."
1383
1364
  },
1384
1365
  {
1385
1366
  "id": "frontend-nextjs-forms",
@@ -1420,7 +1401,7 @@
1420
1401
  "scroll": "#section-3-scroll-animations",
1421
1402
  "accessibility": "#section-7-wcag-accessibility-mandatory"
1422
1403
  },
1423
- "digest": "Component-level motion with Motion for React (the stack table's 'Framer Motion', its former name; motion 12+, current npm major 14): npm install motion and import from 'motion/react'. framer-motion is not deprecated (motion depends on it and re-exports it), but a codebase keeps one import path, motion/react, and never adds framer-motion alongside it. Variants live in motion/variants.ts (fadeIn, slideInUp/Down/Left/Right, scaleIn, staggerContainer, staggerItem, pageTransition) and take durations and easings from @/lib/design-tokens (fast 0.15, base 0.3); never hardcode duration numbers or easing arrays. Scroll reveals: whileInView with viewport once: true for simple cases, useInView for control. Page transitions: AnimatePresence mode='wait' keyed by usePathname(), subtle (y: 8), inside a 'use client' PageTransition provider -- the root app/layout.tsx stays a Server Component (no 'use client', keeps metadata) and just renders the provider. WCAG is mandatory: useReducedMotion() strips x/y/scale and keeps opacity, plus a prefers-reduced-motion CSS fallback. Skeletons use CSS animate-pulse, not motion. Limit motion to 3-5 moments per page. Scroll-driven scenes (ScrollTrigger, Lenis) belong to GSAP; never share an element with it."
1404
+ "digest": "Component-level motion with Motion for React (the stack table's 'Framer Motion', its former name; motion 12+, current npm major 14): npm install motion and import from 'motion/react'. framer-motion is not deprecated (motion depends on it and re-exports it), but a codebase keeps one import path, motion/react (a framer-motion import is a lint error, frontend-nextjs-lint). Variants live in motion/variants.ts (fadeIn, slideInUp/Down/Left/Right, scaleIn, staggerContainer, staggerItem, pageTransition) and take durations and easings from @/lib/design-tokens (fast 0.15, base 0.3); never hardcode duration numbers or easing arrays. Scroll reveals: whileInView with viewport once: true for simple cases, useInView for control. Page transitions: AnimatePresence mode='wait' keyed by usePathname(), inside a 'use client' PageTransition provider -- the root app/layout.tsx stays a Server Component (no 'use client', keeps metadata) and just renders the provider. WCAG is mandatory: useReducedMotion() strips x/y/scale and keeps opacity, plus a prefers-reduced-motion CSS fallback. Scroll-driven scenes (ScrollTrigger, Lenis) belong to GSAP; never share an element with it."
1424
1405
  },
1425
1406
  {
1426
1407
  "id": "frontend-nextjs-naming-conventions",
@@ -1439,7 +1420,7 @@
1439
1420
  "files": "#complete-reference-table",
1440
1421
  "components": "#complete-reference-table"
1441
1422
  },
1442
- "digest": "Next.js naming (prevents Linux case-sensitivity bugs). File names are ALWAYS kebab-case (`user-card.tsx`, never `UserCard.tsx`); exports are PascalCase for components (`UserCard`) and camelCase with a `use` prefix for hooks, whose files are kebab-case too (`use-users.ts` -> `useUsers`, `use-user.ts` for a single GET, `use-create-user.ts`, `use-update-user.ts`, `use-delete-user.ts`). Zod schema files end in `.schemas.ts` (`createUserSchema`) and type files in `.types.ts`, with types derived via `z.infer` instead of hand-written duplicates. Components use named exports (`export function UserCard`, not `export default`); `page.tsx` exports `default`. A feature `index.ts` re-exports. Never mix cases within a category (no `UserCard.tsx` next to `user-profile.tsx`)."
1423
+ "digest": "Next.js naming (prevents Linux case-sensitivity bugs). File names are ALWAYS kebab-case (`user-card.tsx`, never `UserCard.tsx`); components export PascalCase (`UserCard`); hook files are kebab-case too (`use-users.ts` -> `useUsers`, `use-user.ts` for a single GET, `use-create-user.ts`, `use-update-user.ts`, `use-delete-user.ts`). Zod schema files end in `.schemas.ts` (`createUserSchema`) and type files in `.types.ts`, with types derived via `z.infer` instead of hand-written duplicates. A feature `index.ts` re-exports. Named exports and the hook `use` prefix are lint errors, not prose (frontend-nextjs-lint). Never mix cases within a category (no `UserCard.tsx` next to `user-profile.tsx`)."
1443
1424
  },
1444
1425
  {
1445
1426
  "id": "frontend-nextjs-nextjs-patterns",
@@ -1458,7 +1439,7 @@
1458
1439
  "auth": "#route-handlers-bff-pattern",
1459
1440
  "caching": "#react-query--neon"
1460
1441
  },
1461
- "digest": "Next.js 15 App Router (not Pages Router): Server Components by default, `'use client'` only for state, effects, events or browser APIs; colocate `loading.tsx` and `error.tsx` (the latter MUST be `'use client'`) with `page.tsx`. Route Handlers act as a BFF: check `auth.getSession()` (401 if absent) and delegate to the .NET API; database access lives in the .NET backend via EF Core, NEVER in Next.js route handlers. Validate input with Zod on client and server (`safeParse`, 400). Fetch with TanStack Query (`useQuery`/`useMutation`, `staleTime: 60_000`, `retry: 1`, `invalidateQueries` on success), never `useEffect` for data fetching. Forms: react-hook-form + `zodResolver`. shadcn is copied via `npx shadcn@latest add`, not an npm dependency. Keep `\"strict\": true` plus `noUncheckedIndexedAccess` (agents self-correct from compiler errors); no `any` for API responses (Zod schema + `z.infer`)."
1442
+ "digest": "Next.js 15 App Router (not Pages Router): Server Components by default, `'use client'` only for state, effects, events or browser APIs; colocate `loading.tsx` and `error.tsx` with `page.tsx`. Route Handlers act as a BFF: check `auth.getSession()` (401 if absent) and delegate to the .NET API; database access lives in the .NET backend via EF Core (a DB driver in Next is a lint error, frontend-nextjs-lint). Validate input with Zod on client and server (`safeParse`, 400). Fetch with TanStack Query (`useQuery`/`useMutation`, `invalidateQueries` on success). Forms: react-hook-form + `zodResolver`. shadcn is copied via `npx shadcn@latest add`, not an npm dependency. Keep `\"strict\": true` plus `noUncheckedIndexedAccess` (agents self-correct from compiler errors); API responses are a Zod schema + `z.infer`."
1462
1443
  },
1463
1444
  {
1464
1445
  "id": "frontend-nextjs-project-structure",
@@ -1477,7 +1458,7 @@
1477
1458
  "structure": "#canonical-folder-tree",
1478
1459
  "organization": "#feature-index-pattern"
1479
1460
  },
1480
- "digest": "Next.js 15 App Router, src/ layout with the @/ alias, feature-based. src/app/ is routing only (layout, page, loading, error) and imports from features/, never the reverse. Each src/features/{feature-name}/ holds components/, hooks/ (TanStack Query hooks such as use-users.ts, use-create-user.ts), types/ (*.types.ts plus *.schemas.ts with Zod and z.infer) and an index.ts that is the explicit public API: import from '@/features/users', never from deep paths. NEVER import one feature from another (extract to components/ or lib/); components/ must not import features/, is presentation only with NO business logic, and components/ui/ is shadcn, never edited directly. Shared hooks/ make no API calls. lib/ holds api-client.ts (typed fetch wrapper for the .NET API) and query-client.tsx; types/api.ts has PaginatedResponse<T> and ApiError; env.mjs validates env vars with Zod."
1461
+ "digest": "Next.js 15 App Router, src/ layout with the @/ alias, feature-based. src/app/ is routing only (layout, page, loading, error); screen logic lives in features/. Each src/features/{feature-name}/ holds components/, hooks/ (TanStack Query hooks such as use-users.ts, use-create-user.ts), types/ (*.types.ts plus *.schemas.ts with Zod and z.infer) and an index.ts that is the explicit public API. NEVER import one feature from another (extract to components/ or lib/); components/ is presentation only with NO business logic, and components/ui/ is shadcn, never edited directly. Deep imports from outside a feature, components/ -> features/, features/ -> app/ and shared hooks/ -> the API client are lint errors (frontend-nextjs-lint). lib/ holds api-client.ts (typed fetch wrapper for the .NET API) and query-client.tsx; types/api.ts has PaginatedResponse<T> and ApiError; env.mjs validates env vars with Zod."
1481
1462
  },
1482
1463
  {
1483
1464
  "id": "frontend-nextjs-state-management",
@@ -1496,7 +1477,7 @@
1496
1477
  "patterns": "#state-decision-tree",
1497
1478
  "server": "#server-state-tanstack-query"
1498
1479
  },
1499
- "digest": "Server state (anything from the .NET API) lives in TanStack Query, never copied into `useState` via `useEffect`; derive state from server data instead of syncing it. Local UI state (modal open, form step, selected id) uses `useState`. A list's page, sort and filters, and a page's active tab, go in the URL (`searchParams`), never `useState`, so a refresh or shared link reopens the same view. Do not use React Context for server state (no caching or dedup); Context is for truly global theme, auth user or locale, and prop drilling 2-3 levels is fine. NEVER install Zustand or Redux before exhausting Server Components + TanStack Query. Zustand is justified only if ALL hold: state needed in 5+ unrelated components, complex update logic, Context causes measurable re-render problems, and the state is not from the server."
1480
+ "digest": "Server state (anything from the .NET API) lives in TanStack Query, never copied into `useState` via `useEffect`; derive state from server data instead of syncing it. Local UI state (modal open, form step, selected id) uses `useState`. A list's page, sort and filters, and a page's active tab, go in the URL (`searchParams`), never `useState`, so a refresh or shared link reopens the same view. Do not use React Context for server state (no caching or dedup); Context is for truly global theme, auth user or locale. Zustand/Redux imports are lint errors (frontend-nextjs-lint); the one exception is an eslint-disable-next-line with the reason in a single store file, only if ALL hold: state needed in 5+ unrelated components, complex update logic, Context causes measurable re-render problems, and the state is not from the server."
1500
1481
  },
1501
1482
  {
1502
1483
  "id": "frontend-nextjs-testing",
@@ -1830,7 +1811,7 @@
1830
1811
  "not-worth": "#o-que-não-merece",
1831
1812
  "size": "#tamanho"
1832
1813
  },
1833
- "digest": "Default is NO comment: code explains itself through names, types and tests. Before commenting, rename, extract a function or named constant, encode the state in the type, or write a test; comment only what none of these can express: an invariant the type does not express, an order that matters, an external quirk (third-party API, library bug with its link), or the contract of a package published for consumers outside the repo. Never process history (task, UC, ADR, FR, Gate, evaluator, spec.md, mockup: commit message and plan.md (## Decisões); in a test the link to the postcondition goes in the test NAME), never restate the code, no XML doc/JSDoc on application members that repeats the signature, no file headers, section banners, #region, commented-out code or loose TODOs, no claims about behavior (make them test assertions). One line is normal, ~3 is the ceiling; more than one comment line per ten code lines means the names are not doing the work. A heavily commented neighbour file is not a model. The comment-guard hook blocks an edit that ADDS process history, a banner/#region, a block over 3 lines or a doc-comment outside a published package; the verify validators node (code-budget) warns on added comments that cite process and on added comment density."
1814
+ "digest": "Default is NO comment: code explains itself through names, types and tests. Before commenting, rename, extract a function or named constant, encode the state in the type, or write a test; comment only what none of these can express: an invariant the type does not express, an order that matters, an external quirk (third-party API, library bug with its link), or the contract of a package published for consumers outside the repo. Never process history (task, UC, ADR, FR, Gate, evaluator, spec.md, mockup: commit message and plan.md (## Decisões); in a test the link to the postcondition goes in the test NAME), never restate the code, no XML doc/JSDoc on application members that repeats the signature, no file headers, section banners, #region, commented-out code or loose TODOs, no claims about behavior (make them test assertions). One line is normal, ~3 is the ceiling; more than one comment line per ten code lines means the names are not doing the work. A heavily commented neighbour file is not a model. The comment-guard hook blocks an edit that ADDS process history, a banner/#region, commented-out code, a block over 3 lines or a doc-comment outside a published package; the verify validators node (code-budget) warns on added comments that cite process and on added comment density."
1834
1815
  },
1835
1816
  {
1836
1817
  "id": "ai-agents-tool-adapters",
@@ -1926,6 +1907,28 @@
1926
1907
  "not-a-rule": "#o-que-não-é-regra"
1927
1908
  },
1928
1909
  "digest": "Rules with a reason that must never break are build errors, not prose: three files at the solution root, no .csproj change. Directory.Build.props adds Microsoft.CodeAnalysis.BannedApiAnalyzers 5.6.0 (PrivateAssets=all), BannedSymbols.txt as AdditionalFiles and morph.globalconfig as GlobalAnalyzerConfigFiles (with Central Package Management the version goes to Directory.Packages.props). BannedSymbols.txt bans M:System.Guid.NewGuid (use Guid.CreateVersion7(): time-ordered, does not fragment the Postgres PK index). morph.globalconfig sets RS0030 and CA2016 (a CancellationToken received and not forwarded) to error. A new banned API is one BannedSymbols.txt line with the reason after the semicolon, never a prose rule. sealed, primary constructors, var, expression bodies and using order are taste: neither prose nor analyzer; follow the neighbouring file."
1910
+ },
1911
+ {
1912
+ "id": "frontend-nextjs-lint",
1913
+ "name": "lint",
1914
+ "path": "frontend/nextjs/lint.md",
1915
+ "category": "frontend",
1916
+ "subcategory": "nextjs",
1917
+ "tags": [
1918
+ "frontend",
1919
+ "nextjs",
1920
+ "lint",
1921
+ "eslint"
1922
+ ],
1923
+ "stacks": [
1924
+ "nextjs"
1925
+ ],
1926
+ "anchors": {
1927
+ "files": "#arquivos",
1928
+ "rules": "#o-que-cada-regra-compra",
1929
+ "not-a-rule": "#o-que-não-é-regra"
1930
+ },
1931
+ "digest": "Rules with a reason that a machine can see are lint errors, not prose: one eslint.config.mjs at the Next app root, flat config spreading eslint-config-next/core-web-vitals and eslint-config-next/typescript, no new plugin. ESLint 9 (eslint-plugin-import/react peer range; the bundled typescript-eslint wants TypeScript <6.1); Next 16 next build no longer lints; the verify build node runs eslint --quiet on the files the feature changed (legacy lint debt never reddens a feature), from the app root, when eslint.config.* and eslint are present. no-restricted-imports bans framer-motion (one import path, motion/react), zustand/redux/@reduxjs/toolkit (server state is TanStack Query, page and filters in the URL), DB drivers pg/@neondatabase/serverless/@prisma/client/drizzle-orm/postgres (Next never touches the database; the .NET API does), the legacy shadcn @/components/ui/form, and @/features/*/* deep imports outside src/features (use the feature index.ts). import/no-restricted-paths: src/components never imports src/features, src/features never imports src/app, src/hooks never imports the API client (lib/api-client.ts, lib/api). no-restricted-syntax: fetch or apiClient called inside useEffect, and 'use client' in the root app/layout.tsx. import/no-default-export except Next convention files (page, layout, loading, error, not-found, template, default, route, sitemap, robots, icons, OG images) and *.config.*. @typescript-eslint/no-explicit-any error, no-console warn. A new restriction is one line in this file with the reason in its message; a one-off exception is eslint-disable-next-line with the reason. CSS class naming, animation class catalogs, optional className, Zod on props, motion counts and offsets, QueryClient defaults and prop-drilling depth are taste: neither prose nor lint."
1929
1932
  }
1930
1933
  ]
1931
1934
  }
@@ -819,17 +819,6 @@
819
819
  "verifiedAgainst": "CSS animations/transitions (language-level, no framework).",
820
820
  "provedBy": []
821
821
  },
822
- "frontend-design-system-naming": {
823
- "aliases": [
824
- "BEM",
825
- "CSS naming",
826
- "class naming",
827
- "design token naming",
828
- "component naming"
829
- ],
830
- "verifiedAgainst": "CSS (BEM + custom properties — language-level, no framework).",
831
- "provedBy": []
832
- },
833
822
  "frontend-design-system-premium-finish": {
834
823
  "aliases": [
835
824
  "premium finish",
@@ -1251,6 +1240,19 @@
1251
1240
  ],
1252
1241
  "verifiedAgainst": ".NET SDK 10.0.402, Microsoft.CodeAnalysis.BannedApiAnalyzers 5.6.0 (sonda: `Guid.NewGuid()` → erro RS0030, token não repassado → erro CA2016, código conforme → 0 erros).",
1253
1242
  "provedBy": []
1243
+ },
1244
+ "frontend-nextjs-lint": {
1245
+ "aliases": [
1246
+ "eslint",
1247
+ "eslint.config.mjs",
1248
+ "lint rule",
1249
+ "no-restricted-imports",
1250
+ "no-restricted-paths",
1251
+ "eslint-config-next",
1252
+ "default export"
1253
+ ],
1254
+ "verifiedAgainst": "eslint 9.39.5, eslint-config-next 16.4.2 (typescript-eslint 8.71.2, eslint-plugin-import 2.32.0), next 16.4.2, typescript 6.0.3 (sonda: um arquivo ruim por regra → erro da regra esperada, `no-console` → aviso, arquivos conformes e o scaffold `morph-spec scaffold nextjs` → 0 problemas).",
1255
+ "provedBy": []
1254
1256
  }
1255
1257
  },
1256
1258
  "sharedAliases": {
@@ -1323,10 +1325,6 @@
1323
1325
  "architecture-vertical-slice-vertical-slice",
1324
1326
  "backend-dotnet-vsa-handler-patterns"
1325
1327
  ],
1326
- "component naming": [
1327
- "frontend-design-system-naming",
1328
- "frontend-nextjs-naming-conventions"
1329
- ],
1330
1328
  "video generation": [
1331
1329
  "ai-agents-media-video",
1332
1330
  "frontend-design-system-ai-video-generation"
@@ -93,7 +93,7 @@ await upsert({ key: doc.sourceId, hash: sha256(doc.body) });
93
93
  ## Na revisão
94
94
 
95
95
  Na hora da edição, o hook `comment-guard` bloqueia o comentário **adicionado** que é história de
96
- processo, banner/`#region`, bloco de mais de 3 linhas ou `///`/JSDoc em projeto que não é pacote
96
+ processo, banner/`#region`, código comentado, bloco de mais de 3 linhas ou `///`/JSDoc em projeto que não é pacote
97
97
  publicado. Comentário que o arquivo já tinha nunca é acusado. Repetir o código em uma linha não é
98
98
  detectável por forma: fica com a revisão abaixo.
99
99
 
@@ -82,9 +82,7 @@ The page parses the query, renders the toolbar, and wraps the results in a `Susp
82
82
  // app/(admin)/products/page.tsx
83
83
  import { Suspense } from 'react';
84
84
  import { DataTableSkeleton } from '@/components/data-table-skeleton';
85
- import { ProductResults } from '@/features/products/components/product-results';
86
- import { ProductToolbar } from '@/features/products/components/product-toolbar';
87
- import { productListQuerySchema } from '@/features/products/types/product-list.schemas';
85
+ import { ProductResults, ProductToolbar, productListQuerySchema } from '@/features/products';
88
86
 
89
87
  export default async function ProductsPage({
90
88
  searchParams,
@@ -35,10 +35,14 @@ The page reads `tab` from `searchParams` and renders only that tab's content on
35
35
 
36
36
  ```tsx
37
37
  // app/(admin)/products/[id]/page.tsx
38
- import { ProductTabs, PRODUCT_TABS, type ProductTab } from '@/features/products/components/product-tabs';
39
- import { ProductDetailsTab } from '@/features/products/components/product-details-tab';
40
- import { ProductVariantsTab } from '@/features/products/components/product-variants-tab';
41
- import { ProductHistoryTab } from '@/features/products/components/product-history-tab';
38
+ import {
39
+ PRODUCT_TABS,
40
+ ProductDetailsTab,
41
+ ProductHistoryTab,
42
+ ProductTabs,
43
+ ProductVariantsTab,
44
+ type ProductTab,
45
+ } from '@/features/products';
42
46
 
43
47
  export default async function ProductPage({
44
48
  params,
@@ -16,9 +16,9 @@ Next.js App Router with TypeScript strict mode. Server Components by default, Cl
16
16
  - ALWAYS default to Server Components — add `'use client'` only when needed
17
17
  - ALWAYS keep `app/` directory for routing only — no business logic in page files
18
18
  - NEVER put data fetching in Client Components when a Server Component can do it
19
- - NEVER use `useEffect` to fetch data — use Server Components or TanStack Query
20
19
  - ALWAYS co-locate loading/error UI: `loading.tsx`, `error.tsx` next to `page.tsx`
21
20
  - ALWAYS use TypeScript — no `.js` or `.jsx` files in the project
21
+ - Fetch inside `useEffect` and `'use client'` in the root `layout.tsx` are lint errors (`frontend-nextjs-lint`)
22
22
 
23
23
  ## Server vs Client Components
24
24
 
@@ -68,7 +68,7 @@ src/app/
68
68
  | `page.tsx` | Route segment UI | Server Component (default) |
69
69
  | `layout.tsx` | Shared UI wrapper | Server Component |
70
70
  | `loading.tsx` | Suspense fallback | Server Component |
71
- | `error.tsx` | Error boundary | **Must be Client Component** |
71
+ | `error.tsx` | Error boundary | Client Component |
72
72
  | `not-found.tsx` | 404 page | Server Component |
73
73
  | `route.ts` | API route (avoid — use .NET API) | — |
74
74
 
@@ -80,7 +80,7 @@ the same-origin proxy prefix Client Components use. See `frontend/nextjs/data-fe
80
80
 
81
81
  ```tsx
82
82
  // app/(dashboard)/users/page.tsx
83
- import { UserList } from '@/features/users/components/user-list';
83
+ import { UserList } from '@/features/users';
84
84
  import { BACKEND_API_PREFIX } from '@/lib/api/prefixes'; // server-direct prefix ONLY
85
85
 
86
86
  async function getUsers(): Promise<User[]> {
@@ -123,7 +123,6 @@ export default function RootLayout({ children }: { children: React.ReactNode })
123
123
  | Wrong | Right | Why |
124
124
  |-------|-------|-----|
125
125
  | `'use client'` on every component | Only add when needed | Kills SSR benefits, bloats JS bundle |
126
- | `useEffect(() => { fetch(...) }, [])` | Server Component fetch or TanStack Query | Two renders, no caching, no SSR |
127
126
  | Business logic in `page.tsx` | Move to `features/` | pages are routes, not controllers |
128
127
  | `fetch` without error handling | Always check `res.ok` | Silent failures in production |
129
128
 
@@ -14,10 +14,10 @@ Three-tier component hierarchy. `components/ui/` is shadcn primitives (never edi
14
14
  ## Core Rules
15
15
 
16
16
  - NEVER edit files in `components/ui/` — they are regenerated by shadcn CLI
17
- - NEVER import from `features/` inside `components/` — components know nothing about domain
18
17
  - ALWAYS compose shadcn primitives in `components/` instead of editing them
19
18
  - ALWAYS add `'use client'` only if the component uses hooks, events, or browser APIs
20
19
  - NEVER pass raw API data directly to a component — transform to props first
20
+ - `components/` importing `features/` and the legacy `@/components/ui/form` are lint errors (`frontend-nextjs-lint`)
21
21
 
22
22
  ## Three-Tier Hierarchy
23
23
 
@@ -89,26 +89,13 @@ export function UsersPageHeader({ canInvite }: { canInvite: boolean }) {
89
89
 
90
90
  A feature **list** is a Tier 3 component too: the feature owns the columns and the URL ⇄ table mapping, the shared table owns the rendering — `admin-app/data-table.md` → "The Feature Table".
91
91
 
92
- ## Component Props Conventions
93
-
94
- ```tsx
95
- interface UserCardProps {
96
- user: User;
97
- onEdit?: (id: string) => void;
98
- className?: string; // Always allow className for Tailwind override
99
- }
100
-
101
- export function UserCard({ user, onEdit, className }: UserCardProps) {}
102
- ```
103
-
104
92
  ## Common Mistakes
105
93
 
106
94
  | Wrong | Right | Why |
107
95
  |-------|-------|-----|
108
96
  | Edit `components/ui/button.tsx` | Wrap in `components/action-button.tsx` | shadcn CLI overwrites it |
109
- | `import { useUsers } from '@/features/users'` in `components/` | Move to a feature component | Breaks tier isolation |
110
97
  | `'use client'` on all components | Only on interactive ones | Unnecessary client JS |
111
- | Pass raw fetch response as prop | Type and validate with Zod first | Runtime type safety |
98
+ | Pass raw fetch response as prop | Map it to typed props in the feature | The component stops depending on the API shape |
112
99
 
113
100
  ---
114
101