@polymorphism-tech/morph-spec 9.0.0 → 9.1.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 (304) hide show
  1. package/CLAUDE.md +8 -9
  2. package/README.md +38 -36
  3. package/bin/morph-spec.js +4 -62
  4. package/bin/validate.js +1 -39
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +1 -1
  7. package/docs/QUICKSTART.md +1 -1
  8. package/framework/CLAUDE.md +1 -1
  9. package/framework/MORPH.md +1 -1
  10. package/framework/agents.json +12 -4
  11. package/framework/commands/morph-apply.md +1 -1
  12. package/framework/commands/morph-status.md +1 -1
  13. package/framework/docs/CLI.md +6 -17
  14. package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +6 -49
  15. package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -35
  16. package/framework/hooks/claude-code/core/chain.js +4 -65
  17. package/framework/hooks/claude-code/core/comment-guard.js +2 -16
  18. package/framework/hooks/claude-code/core/enforce-phase-writes.js +6 -50
  19. package/framework/hooks/claude-code/core/gate-guard.js +7 -67
  20. package/framework/hooks/claude-code/core/graph-refresh-kick.js +3 -24
  21. package/framework/hooks/claude-code/core/heavy-gate.js +3 -21
  22. package/framework/hooks/claude-code/core/heavy-release.js +2 -13
  23. package/framework/hooks/claude-code/core/loop-detect.js +2 -31
  24. package/framework/hooks/claude-code/core/pre-bash-denylist.js +0 -4
  25. package/framework/hooks/claude-code/core/protect-spec-files.js +7 -67
  26. package/framework/hooks/claude-code/core/state-sync.js +11 -67
  27. package/framework/hooks/claude-code/core/telemetry-log.js +12 -93
  28. package/framework/hooks/claude-code/core/validate-completion.js +11 -57
  29. package/framework/hooks/claude-code/core/warn-bash-source-write.js +1 -9
  30. package/framework/hooks/claude-code/core/worktree-context.js +10 -110
  31. package/framework/hooks/claude-code/core/worktree-create.js +6 -70
  32. package/framework/hooks/claude-code/core/worktree-env-inject.js +5 -56
  33. package/framework/hooks/claude-code/core/worktree-remove.js +5 -31
  34. package/framework/hooks/shared/activity-logger.js +9 -89
  35. package/framework/hooks/shared/agent-reap.js +2 -17
  36. package/framework/hooks/shared/build-check.js +6 -75
  37. package/framework/hooks/shared/claim-registry.js +9 -119
  38. package/framework/hooks/shared/command-tokens.js +6 -51
  39. package/framework/hooks/shared/comment-rules.js +5 -20
  40. package/framework/hooks/shared/comment-scanner.js +6 -25
  41. package/framework/hooks/shared/comment-stripper.js +11 -85
  42. package/framework/hooks/shared/e2e-check.js +0 -0
  43. package/framework/hooks/shared/errata.js +2 -15
  44. package/framework/hooks/shared/feature-locator.js +11 -95
  45. package/framework/hooks/shared/frontend-locations.js +1 -3
  46. package/framework/hooks/shared/gate-question.js +1 -7
  47. package/framework/hooks/shared/git-exclude.js +3 -20
  48. package/framework/hooks/shared/git-paths.js +8 -104
  49. package/framework/hooks/shared/graph-freshness.js +22 -166
  50. package/framework/hooks/shared/graph-refresh.js +21 -149
  51. package/framework/hooks/shared/heavy-command.js +4 -12
  52. package/framework/hooks/shared/hook-response.js +8 -83
  53. package/framework/hooks/shared/morph-cli.js +8 -52
  54. package/framework/hooks/shared/payload-utils.js +10 -132
  55. package/framework/hooks/shared/phase-utils.js +1 -42
  56. package/framework/hooks/shared/redact-secrets.js +4 -15
  57. package/framework/hooks/shared/reparse-scan.js +6 -66
  58. package/framework/hooks/shared/slice-progress.js +4 -23
  59. package/framework/hooks/shared/source-files.js +4 -17
  60. package/framework/hooks/shared/state-reader.js +13 -183
  61. package/framework/hooks/shared/state-writer.js +8 -52
  62. package/framework/hooks/shared/stdin-reader.js +1 -11
  63. package/framework/hooks/shared/telemetry-logger.js +7 -67
  64. package/framework/hooks/shared/transcript-usage.js +7 -29
  65. package/framework/hooks/shared/worktree-root.js +7 -29
  66. package/framework/rules/csharp-standards.md +1 -1
  67. package/framework/rules/frontend-standards.md +1 -0
  68. package/framework/rules/hangfire-standards.md +1 -1
  69. package/framework/rules/neon-standards.md +1 -1
  70. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  71. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  72. package/framework/skills/core/morph-apply/SKILL.md +2 -2
  73. package/framework/skills/core/morph-hotfix/SKILL.md +1 -1
  74. package/framework/skills/core/morph-machine/SKILL.md +1 -1
  75. package/framework/skills/core/morph-media/SKILL.md +1 -1
  76. package/framework/skills/core/morph-plan/SKILL.md +2 -2
  77. package/framework/skills/core/morph-review/SKILL.md +1 -1
  78. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  79. package/framework/skills/core/morph-uiux/SKILL.md +29 -12
  80. package/framework/standards/STANDARDS.json +34 -12
  81. package/framework/standards/STANDARDS.meta.json +16 -2
  82. package/framework/standards/ai-agents/agent-archetypes.md +2 -2
  83. package/framework/standards/ai-agents/agent-session.md +1 -1
  84. package/framework/standards/ai-agents/conversational-agent-with-phases.md +4 -4
  85. package/framework/standards/ai-agents/durable-workflows-hitl.md +3 -3
  86. package/framework/standards/ai-agents/guardrails.md +1 -1
  87. package/framework/standards/ai-agents/multi-agent-patterns.md +2 -2
  88. package/framework/standards/ai-agents/observability-patterns.md +1 -1
  89. package/framework/standards/ai-agents/production.md +1 -1
  90. package/framework/standards/ai-agents/prompt-sources.md +2 -2
  91. package/framework/standards/ai-agents/providers/model-registry.md +1 -1
  92. package/framework/standards/ai-agents/rag-custom-pgvector.md +2 -2
  93. package/framework/standards/ai-agents/setup.md +2 -2
  94. package/framework/standards/ai-agents/sweet-spot.md +2 -2
  95. package/framework/standards/ai-agents/tool-adapters.md +0 -2
  96. package/framework/standards/ai-agents/vector-data-extensions.md +4 -4
  97. package/framework/standards/ai-agents/workflows.md +1 -1
  98. package/framework/standards/architecture/code-comments.md +1 -1
  99. package/framework/standards/architecture/vertical-slice/vertical-slice.md +1 -1
  100. package/framework/standards/backend/api/validation.md +1 -1
  101. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -5
  102. package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +1 -1
  103. package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -2
  104. package/framework/standards/data/nosql/cache/redis.md +1 -1
  105. package/framework/standards/data/nosql/cosmos-db.md +3 -3
  106. package/framework/standards/frontend/admin-app/forms.md +1 -1
  107. package/framework/standards/frontend/design-system/art-direction.md +88 -0
  108. package/framework/standards/frontend/design-system/premium-finish.md +77 -2
  109. package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
  110. package/framework/templates/CLAUDE.md.core.template +3 -3
  111. package/framework/templates/REGISTRY.json +9 -9
  112. package/framework/templates/docs/ui-mockups.md +4 -0
  113. package/package.json +1 -1
  114. package/src/commands/context/context-refresh.js +0 -16
  115. package/src/commands/dashboard/dashboard.js +2 -8
  116. package/src/commands/e2e/e2e.js +17 -104
  117. package/src/commands/graph/affected.js +0 -14
  118. package/src/commands/graph/build.js +11 -50
  119. package/src/commands/graph/explain.js +0 -16
  120. package/src/commands/graph/status.js +1 -12
  121. package/src/commands/mcp/mcp-setup.js +4 -62
  122. package/src/commands/observability/cost.js +5 -57
  123. package/src/commands/observability/telemetry.js +1 -14
  124. package/src/commands/project/branch.js +4 -45
  125. package/src/commands/project/claim.js +4 -48
  126. package/src/commands/project/doctor.js +48 -302
  127. package/src/commands/project/env.js +1 -27
  128. package/src/commands/project/fleet.js +3 -32
  129. package/src/commands/project/init.js +4 -68
  130. package/src/commands/project/install-plugin-cmd.js +0 -1
  131. package/src/commands/project/standards.js +1 -62
  132. package/src/commands/project/status.js +0 -53
  133. package/src/commands/project/update.js +31 -239
  134. package/src/commands/project/worktree.js +108 -882
  135. package/src/commands/scaffold/scaffold.js +0 -25
  136. package/src/commands/state/advance.js +14 -85
  137. package/src/commands/state/approve.js +14 -96
  138. package/src/commands/state/archive.js +12 -64
  139. package/src/commands/state/auto-approval.js +2 -5
  140. package/src/commands/state/create.js +22 -109
  141. package/src/commands/state/delete.js +11 -63
  142. package/src/commands/state/finish.js +98 -475
  143. package/src/commands/state/gate-check.js +22 -132
  144. package/src/commands/state/gate-decision.js +3 -39
  145. package/src/commands/state/handoff.js +3 -30
  146. package/src/commands/state/list.js +14 -84
  147. package/src/commands/state/report-check.js +8 -58
  148. package/src/commands/state/retype.js +4 -26
  149. package/src/commands/validation/index.js +0 -3
  150. package/src/commands/validation/mutate.js +2 -5
  151. package/src/commands/validation/validate-feature.js +5 -94
  152. package/src/commands/validation/verify.js +15 -98
  153. package/src/core/paths/output-schema.js +2 -101
  154. package/src/core/state/feature-identity.js +1 -25
  155. package/src/core/state/gate-order.js +0 -27
  156. package/src/core/state/lifecycle-audit.js +8 -100
  157. package/src/core/state/state-manager.js +22 -304
  158. package/src/core/templates/template-registry.js +2 -97
  159. package/src/core/templates/template-renderer.js +13 -93
  160. package/src/core/templates/template-seed.js +0 -1
  161. package/src/core/work-types.js +1 -36
  162. package/src/dashboard/app/App.jsx +1 -8
  163. package/src/dashboard/app/components/ActivityFeed.jsx +0 -5
  164. package/src/dashboard/app/components/CommandPalette.jsx +0 -7
  165. package/src/dashboard/app/components/DetailPanel.jsx +1 -5
  166. package/src/dashboard/app/components/EntityLink.jsx +0 -12
  167. package/src/dashboard/app/components/ErrorBoundary.jsx +1 -8
  168. package/src/dashboard/app/components/KnowledgeMarkdown.jsx +0 -11
  169. package/src/dashboard/app/components/Markdown.jsx +0 -9
  170. package/src/dashboard/app/components/PhaseRail.jsx +0 -11
  171. package/src/dashboard/app/components/SearchFilter.jsx +0 -4
  172. package/src/dashboard/app/components/Sidebar.jsx +0 -5
  173. package/src/dashboard/app/hooks/useMorphQuery.js +1 -11
  174. package/src/dashboard/app/hooks/usePref.js +1 -5
  175. package/src/dashboard/app/lib/format.js +0 -2
  176. package/src/dashboard/app/lib/phases.js +1 -5
  177. package/src/dashboard/app/lib/project.js +0 -12
  178. package/src/dashboard/app/pages/AgentMap.jsx +1 -8
  179. package/src/dashboard/app/pages/FeatureDetail.jsx +1 -5
  180. package/src/dashboard/app/pages/Features.jsx +1 -8
  181. package/src/dashboard/app/pages/Guide.jsx +0 -1
  182. package/src/dashboard/app/pages/HookInspector.jsx +2 -23
  183. package/src/dashboard/app/pages/PhasePipeline.jsx +0 -3
  184. package/src/dashboard/app/pages/Portfolio.jsx +0 -1
  185. package/src/dashboard/app/pages/RulesMap.jsx +1 -16
  186. package/src/dashboard/app/pages/SkillsLibrary.jsx +1 -16
  187. package/src/dashboard/app/pages/Standards.jsx +2 -17
  188. package/src/dashboard/scanner/activity-scanner.js +10 -69
  189. package/src/dashboard/scanner/agents-scanner.js +1 -23
  190. package/src/dashboard/scanner/docs-scanner.js +0 -6
  191. package/src/dashboard/scanner/feature-status.js +2 -5
  192. package/src/dashboard/scanner/features-scanner.js +10 -66
  193. package/src/dashboard/scanner/hooks-scanner.js +5 -28
  194. package/src/dashboard/scanner/index.js +0 -4
  195. package/src/dashboard/scanner/portfolio.js +5 -29
  196. package/src/dashboard/scanner/project-api.js +8 -54
  197. package/src/dashboard/scanner/rules-scanner.js +1 -2
  198. package/src/dashboard/scanner/skills-scanner.js +2 -12
  199. package/src/dashboard/scanner/standards-scanner.js +2 -6
  200. package/src/dashboard/vite-plugin-morph-data.js +5 -19
  201. package/src/lib/ai/ai-checks.js +7 -52
  202. package/src/lib/ai/ai-project-scan.js +14 -108
  203. package/src/lib/ai/code-audit.js +7 -63
  204. package/src/lib/ai/pin-drift.js +8 -51
  205. package/src/lib/ai/registry-audit.js +11 -90
  206. package/src/lib/ci/ci-verify.js +4 -19
  207. package/src/lib/ci/github-ci.js +9 -37
  208. package/src/lib/ci/workflow-scaffold.js +2 -10
  209. package/src/lib/detectors/claude-config-detector.js +13 -74
  210. package/src/lib/detectors/index.js +4 -50
  211. package/src/lib/e2e/compose-env.js +5 -66
  212. package/src/lib/e2e/stack-teardown.js +6 -40
  213. package/src/lib/generators/architecture-scanner.js +18 -156
  214. package/src/lib/generators/handoff-generator.js +9 -53
  215. package/src/lib/generators/scaffold-generator.js +5 -101
  216. package/src/lib/generators/settings-generator.js +4 -37
  217. package/src/lib/git/branch-classifier.js +11 -84
  218. package/src/lib/git/default-branch.js +14 -169
  219. package/src/lib/git/evidence-staleness.js +1 -0
  220. package/src/lib/git/gh.js +2 -17
  221. package/src/lib/git/pending-prs.js +6 -37
  222. package/src/lib/git/pr-files.js +3 -28
  223. package/src/lib/git/pr-index.js +3 -34
  224. package/src/lib/git/tree-fingerprint.js +3 -15
  225. package/src/lib/git/tree-state.js +6 -33
  226. package/src/lib/graph/god-nodes.js +2 -22
  227. package/src/lib/graph/graph-health.js +3 -24
  228. package/src/lib/graph/graph-loader.js +5 -56
  229. package/src/lib/graph/graphify-runtime.js +7 -37
  230. package/src/lib/graph/graphifyignore.js +8 -74
  231. package/src/lib/graph/sanitize.js +16 -136
  232. package/src/lib/heavy/cleanup.js +1 -1
  233. package/src/lib/installers/effects.js +8 -93
  234. package/src/lib/installers/mcp-installer.js +9 -169
  235. package/src/lib/installers/memory-installer.js +4 -27
  236. package/src/lib/installers/templates-reconcile.js +5 -60
  237. package/src/lib/mutation/dotnet-introspection.js +20 -106
  238. package/src/lib/mutation/subset.js +1 -4
  239. package/src/lib/plan/artifact-vocabulary.js +5 -25
  240. package/src/lib/plan/checks.js +5 -31
  241. package/src/lib/plan/plan-parser.js +1 -6
  242. package/src/lib/projects/registry.js +7 -44
  243. package/src/lib/review-reports.js +5 -23
  244. package/src/lib/risk-detector.js +39 -173
  245. package/src/lib/specs/adr-index.js +6 -34
  246. package/src/lib/specs/frontmatter.js +2 -15
  247. package/src/lib/specs/gfm-table.js +1 -7
  248. package/src/lib/specs/revocations.js +1 -14
  249. package/src/lib/specs/screen-checklist.js +47 -124
  250. package/src/lib/stack/dotnet-build-ignores.js +2 -6
  251. package/src/lib/stack/stack-profile.js +1 -28
  252. package/src/lib/stack/stack-scan.js +1 -8
  253. package/src/lib/stack-filter.js +4 -27
  254. package/src/lib/standards/registry.js +1 -7
  255. package/src/lib/standards/sections.js +2 -13
  256. package/src/lib/standards/verified-against.js +19 -191
  257. package/src/lib/tasks/eval-node.js +21 -169
  258. package/src/lib/tasks/linux-runner.js +18 -142
  259. package/src/lib/tasks/test-nodes.js +43 -240
  260. package/src/lib/tasks/test-runner.js +43 -341
  261. package/src/lib/telemetry/metrics.js +7 -36
  262. package/src/lib/telemetry/telemetry-reader.js +13 -125
  263. package/src/lib/telemetry/verify-event.js +2 -30
  264. package/src/lib/validators/code-budget/code-budget-validator.js +3 -43
  265. package/src/lib/validators/code-budget/index.js +0 -7
  266. package/src/lib/validators/contracts/contract-compliance-validator.js +2 -24
  267. package/src/lib/validators/contracts/index.js +0 -6
  268. package/src/lib/validators/nextjs/index.js +0 -5
  269. package/src/lib/validators/nextjs/next-component-validator.js +4 -81
  270. package/src/lib/validators/packages/index.js +0 -6
  271. package/src/lib/validators/packages/package-validator.js +6 -56
  272. package/src/lib/validators/reuse/index.js +0 -8
  273. package/src/lib/validators/reuse/reuse-validator.js +11 -101
  274. package/src/lib/validators/reuse/symbol-scanner.js +6 -62
  275. package/src/lib/validators/traceability-check.js +5 -63
  276. package/src/lib/validators/validation-runner.js +20 -125
  277. package/src/lib/validators/verify-runner.js +52 -358
  278. package/src/lib/work-type-detector.js +5 -67
  279. package/src/lib/worktree/docker-audit.js +17 -136
  280. package/src/lib/worktree/env-allocator.js +10 -98
  281. package/src/lib/worktree/feature-truth.js +12 -115
  282. package/src/lib/worktree/finish-backup.js +32 -242
  283. package/src/lib/worktree/fleet-scanner.js +13 -95
  284. package/src/lib/worktree/ignored-triage.js +15 -91
  285. package/src/lib/worktree/vscode-settings.js +2 -3
  286. package/src/lib/worktree/worktree-audit.js +21 -213
  287. package/src/lib/worktree/worktree-include.js +3 -5
  288. package/src/scripts/global-install.js +3 -9
  289. package/src/scripts/install-plugin.js +1 -13
  290. package/src/scripts/setup-infra.js +17 -147
  291. package/src/utils/agents-installer.js +28 -275
  292. package/src/utils/banner.js +4 -25
  293. package/src/utils/claude-md-injector.js +1 -37
  294. package/src/utils/claude-settings-manager.js +1 -52
  295. package/src/utils/file-copier.js +11 -95
  296. package/src/utils/hooks-installer.js +21 -298
  297. package/src/utils/skills-installer.js +4 -101
  298. package/src/utils/version-checker.js +4 -83
  299. package/framework/commands/morph-proposal.md +0 -9
  300. package/src/commands/utils/index.js +0 -3
  301. package/src/lib/detectors/design-system-detector.js +0 -188
  302. package/src/lib/generators/design-system-generator.js +0 -508
  303. package/src/lib/specs/old-format.js +0 -68
  304. package/src/utils/agent-id-mapper.js +0 -11
@@ -1,29 +1,13 @@
1
- /**
2
- * worktree-root.js — onde as worktrees do morph-spec moram, num lugar só.
3
- *
4
- * Padrão: `<raiz>/worktrees/<nome>` (mais o legado `.worktrees/`). Com `worktree.root` na
5
- * config (ou `MORPH_WORKTREE_ROOT`), as novas nascem em `<root>/<chave-do-repo>/<nome>`, FORA da
6
- * árvore aberta no editor. A chave é `basename-<hash da raiz>`: só o basename colidiria entre dois
7
- * repositórios de mesmo nome que dividem o mesmo root.
8
- *
9
- * Um root DENTRO da raiz é recusado: toda lista que se orienta pelo nome do container
10
- * (.gitignore, graphifyignore, descoberta de testes, tar do --linux, globs do editor) deixaria de
11
- * enxergá-lo, em silêncio. Recusado, vale o padrão e o motivo volta em `warning`.
12
- *
13
- * O predicado "está no container" é a UNIÃO do layout configurado com o padrão e o legado: uma
14
- * worktree que já existe continua reconhecida depois que a chave muda. Mora em `hooks/shared`
15
- * porque hook não importa `src/`; o `src/` importa daqui.
16
- *
17
- * @module worktree-root
18
- */
1
+ // "Está no container" é a UNIÃO do layout configurado com o padrão e o legado:
2
+ // uma worktree antiga segue reconhecida. Root dentro da raiz é recusado, porque as
3
+ // listas orientadas pelo nome `worktrees/` deixariam de enxergá-lo.
19
4
 
20
5
  import { createHash } from 'crypto';
21
6
  import { readFileSync } from 'fs';
22
7
  import { homedir } from 'os';
23
8
  import { basename, isAbsolute, join, relative, resolve } from 'path';
24
9
 
25
- // No leading dot (C1): Jest escapes it inside an absolute `rootDir` glob, so `.worktrees/` made a whole
26
- // frontend suite report 0 tests without an error. `.worktrees/` is the pre-C1 name, still read everywhere.
10
+ // No leading dot: Jest escapes it inside an absolute `rootDir` glob and reports 0 tests.
27
11
  export const WORKTREES_DIR = 'worktrees';
28
12
  export const LEGACY_WORKTREES_DIR = '.worktrees';
29
13
  export const WORKTREE_DIR_NAMES = [WORKTREES_DIR, LEGACY_WORKTREES_DIR];
@@ -31,7 +15,7 @@ export const WORKTREE_DIR_NAMES = [WORKTREES_DIR, LEGACY_WORKTREES_DIR];
31
15
  const fwd = (p) => String(p).replace(/\\/g, '/');
32
16
  const foldCase = (p) => (process.platform === 'win32' ? p.toLowerCase() : p);
33
17
 
34
- /** `basename-<8 hex do hash da raiz>` — estável entre `R:` e `r:` no Windows. */
18
+ // Só o basename colidiria entre repositórios; o hash é estável entre `R:` e `r:`.
35
19
  export function repoKey(primaryRoot) {
36
20
  const root = fwd(resolve(primaryRoot));
37
21
  const hash = createHash('sha1').update(foldCase(root)).digest('hex').slice(0, 8);
@@ -54,11 +38,7 @@ function isInside(root, path) {
54
38
  return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));
55
39
  }
56
40
 
57
- /**
58
- * @param {string} primaryRoot
59
- * @param {{ env?: NodeJS.ProcessEnv }} [opts]
60
- * @returns {{ dir: string|null, base?: string, warning?: string }} `dir` = `<root>/<repoKey>`
61
- */
41
+ /** @returns {{ dir: string|null, base?: string, warning?: string }} `dir` = `<root>/<repoKey>` */
62
42
  export function externalContainer(primaryRoot, { env = process.env } = {}) {
63
43
  const configured = configuredRoot(primaryRoot, env);
64
44
  if (!configured) return { dir: null };
@@ -70,19 +50,17 @@ export function externalContainer(primaryRoot, { env = process.env } = {}) {
70
50
  return { dir: fwd(join(base, repoKey(primaryRoot))), base: fwd(base) };
71
51
  }
72
52
 
73
- /** Where a NEW worktree lands. */
74
53
  export function newWorktreesDir(primaryRoot, opts) {
75
54
  return externalContainer(primaryRoot, opts).dir ?? `${fwd(resolve(primaryRoot))}/${WORKTREES_DIR}`;
76
55
  }
77
56
 
78
- /** Every directory that counts as "the container", external first. Forward slashes. */
79
57
  export function containerDirs(primaryRoot, opts) {
80
58
  const root = fwd(resolve(primaryRoot));
81
59
  const external = externalContainer(primaryRoot, opts).dir;
82
60
  return [...(external ? [external] : []), ...WORKTREE_DIR_NAMES.map((name) => `${root}/${name}`)];
83
61
  }
84
62
 
85
- /** Is `path` under any of `dirs` (a `containerDirs` result)? Ask `containerDirs` once per audit, not once per path. */
63
+ // Ask `containerDirs` once per audit, not once per path.
86
64
  export function isUnder(dirs, path) {
87
65
  const target = foldCase(fwd(resolve(path)));
88
66
  return dirs.some((dir) => target.startsWith(`${foldCase(dir)}/`));
@@ -9,7 +9,7 @@ stacks:
9
9
  # C# and .NET VSA Standards
10
10
 
11
11
  - **architecture-vertical-slice-vertical-slice** (`architecture/vertical-slice/vertical-slice.md`) — Uma slice por operação em `Features/{Entity}Feature/{Verb}{Entity}/`: Request/Response `sealed record`, Handler, Validator e Endpoint `IApiEndpoint` `internal sealed`. Decorators Scrutor: Logging → Validation → Handler. PROIBIDO: MediatR/`IRequest`, AggregateRoot, DomainEvent, CQRS formal, camada Application Service, DTO compartilhado entre features, `IRepository<T>`/`IUnitOfWork`.
12
- - **backend-dotnet-vsa-handler-patterns** (`backend/dotnet/vsa-handler-patterns.md`) — Handler `sealed class` com primary constructor implementando `IHandler<TRequest, TResponse>` (não MediatR): injeta `ApplicationDbContext` direto, usa `Guid.CreateVersion7()` (nunca `Guid.NewGuid()`), passa `CancellationToken` em tudo e devolve `Result<T>`. Erros em `{Feature}Errors.cs`. SQL cru ou store dedicado só com justificativa em `decisions.md`.
12
+ - **backend-dotnet-vsa-handler-patterns** (`backend/dotnet/vsa-handler-patterns.md`) — Handler `sealed class` com primary constructor implementando `IHandler<TRequest, TResponse>` (não MediatR): injeta `ApplicationDbContext` direto, usa `Guid.CreateVersion7()` (nunca `Guid.NewGuid()`), passa `CancellationToken` em tudo e devolve `Result<T>`. Erros em `{Feature}Errors.cs`. SQL cru ou store dedicado só com justificativa em `plan.md` (`## Decisões`).
13
13
  - **backend-dotnet-result-pattern** (`backend/dotnet/result-pattern.md`) — Falha esperada é `Result`/`Result<T>`, nunca exceção. Endpoint: `result.Match(value => Results.Ok(value), error => error.ToHttpResult())`; nunca leia `.Value` fora do `Match`. Status pelo `ErrorType` (Validation 400, NotFound 404, Conflict 409, Unauthorized 401, Forbidden 403, Unexpected 500), nunca BadRequest para tudo. PROIBIDO: try/catch no handler, retornar null, erro string sem código.
14
14
  - **backend-dotnet-fluent-validation-vsa** (`backend/dotnet/fluent-validation-vsa.md`) — Um `sealed AbstractValidator<TRequest>` (FluentValidation 12) por request, na pasta da slice; o `ValidationDecorator` roda antes do handler e devolve `Result.Failure(ValidationError)`. `AddValidatorsFromAssembly()` ANTES de `AddHandlersFromAssembly()`. Sempre `.WithMessage()`; tempo via `TimeProvider` lido DENTRO da regra. Nunca lance `ValidationException`.
15
15
  - **backend-dotnet-scrutor** (`backend/dotnet/scrutor.md`) — `AddHandlersFromAssembly` registra cada `IHandler<,>` como Scoped (filtro `!t.ContainsGenericParameters`) e depois `Decorate` Validation e então Logging (o último registrado é o mais externo). Endpoints `IApiEndpoint` via `TryAddEnumerable` Transient + `MapApiEndpoints`. Nunca registre handler um a um.
@@ -11,5 +11,6 @@ stacks:
11
11
 
12
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
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.
14
+ - **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.
14
15
 
15
16
  > 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>`.
@@ -10,6 +10,6 @@ stacks:
10
10
 
11
11
  # Hangfire Background Jobs Standards
12
12
 
13
- - **backend-integrations-hangfire-hangfire-jobs** (`backend/integrations/hangfire/hangfire-jobs.md`) — Hangfire 1.8.25 só com razão real em `decisions.md`; o que cabe no request fica no handler. Storage `Hangfire.PostgreSql` 1.21.1 com `UseNpgsqlConnection` no endpoint DIRETO do Neon; referencie `Newtonsoft.Json` 13.0.4. Job é classe fina e idempotente que chama o handler; `[AutomaticRetry]`, sem try/catch nem retry manual. Dashboard exige Admin autenticado.
13
+ - **backend-integrations-hangfire-hangfire-jobs** (`backend/integrations/hangfire/hangfire-jobs.md`) — Hangfire 1.8.25 só com razão real em `plan.md` (`## Decisões`); o que cabe no request fica no handler. Storage `Hangfire.PostgreSql` 1.21.1 com `UseNpgsqlConnection` no endpoint DIRETO do Neon; referencie `Newtonsoft.Json` 13.0.4. Job é classe fina e idempotente que chama o handler; `[AutomaticRetry]`, sem try/catch nem retry manual. Dashboard exige Admin autenticado.
14
14
 
15
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>`.
@@ -11,7 +11,7 @@ stacks:
11
11
  # Neon PostgreSQL Standards
12
12
 
13
13
  - **infrastructure-neon-setup** (`infrastructure/neon/neon-setup.md`) — Duas connection strings: `DefaultConnection` (pooled `-pooler`) para a app e `MigrationConnection` (direta) para migrations e advisory locks; nunca commitadas (user-secrets / env do Coolify). Formato keyword (`Host=...;SSL Mode=Require`), não URI. `EnableRetryOnFailure(5, 10s)` por causa do scale-to-zero. Branch sempre com `--expires-at`.
14
- - **infrastructure-neon-pgvector** (`infrastructure/neon/neon-pgvector.md`) — `Npgsql.EntityFrameworkCore.PostgreSQL` 10.0.3 + `Pgvector.EntityFrameworkCore` 0.3.0; coluna `Vector` (`vector(1536)`) e `UseVector()` no `UseNpgsql`. SEMPRE índice HNSW com operator class casando a função LINQ (senão seq scan); distância em ordem ASC. PROIBIDO: DocumentRepository, SQL cru do C# sem `decisions.md`, default `gen_random_uuid()`.
14
+ - **infrastructure-neon-pgvector** (`infrastructure/neon/neon-pgvector.md`) — `Npgsql.EntityFrameworkCore.PostgreSQL` 10.0.3 + `Pgvector.EntityFrameworkCore` 0.3.0; coluna `Vector` (`vector(1536)`) e `UseVector()` no `UseNpgsql`. SEMPRE índice HNSW com operator class casando a função LINQ (senão seq scan); distância em ordem ASC. PROIBIDO: DocumentRepository, SQL cru do C# sem `plan.md` (`## Decisões`), default `gen_random_uuid()`.
15
15
  - **infrastructure-neon-pgrag** (`infrastructure/neon/neon-pgrag.md`) — A extensão `rag` é EXPERIMENTAL: protótipo e RAG só-SQL, não produção sem teste. Nunca misture modelos de embedding numa coluna (bge-small 384 × text-embedding-3-small 1536). Índice HNSW depois da carga inicial. Produção .NET com lógica própria: tool MAF tipada sobre pgvector.
16
16
  - **backend-integrations-neon-auth-neon-auth** (`backend/integrations/neon-auth/neon-auth.md`) — SDK `@neondatabase/auth`: `createNeonAuth()` no server, `createAuthClient()` no client, `auth.middleware()` com `runtime='nodejs'`. Duas constantes de prefixo (`BACKEND_API_PREFIX`/`PROXY_API_PREFIX`), nunca uma só. O front não consulta o Neon para dado de negócio. .NET: token EdDSA/Ed25519, verificado pelo JWKS com BouncyCastle + `SignatureValidator` devolvendo `JsonWebToken`.
17
17
 
@@ -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.0.0"
7
+ cliVersion: "9.1.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.0.0"
7
+ cliVersion: "9.1.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.0.0"
7
+ cliVersion: "9.1.0"
8
8
  ---
9
9
 
10
10
  # morph-apply
@@ -64,7 +64,7 @@ O hook `validate-completion` (no Stop) e o `report-check` cobram o relatório pe
64
64
 
65
65
  `**Arquivos:**` lista todo arquivo criado ou modificado; cada `C{n}` que a fatia `cobre` aparece pelo
66
66
  id; relatório que abre com `PARCIAL` não conta como feito. Os relatórios **são** o registro da
67
- implementação (não há `recap.md`): o `verify` confere a união dos `**Arquivos:**` contra os
67
+ implementação: o `verify` confere a união dos `**Arquivos:**` contra os
68
68
  `arquivos:` das fatias (aviso, nunca reprova). `**Standards:**` nomeia os ids que o agente de fato
69
69
  consultou (o `report-check` não o exige).
70
70
  - Persona não registrada: `general-purpose` com o campo `persona` de `agents.json` colado, e
@@ -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.0.0"
7
+ cliVersion: "9.1.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.0.0"
7
+ cliVersion: "9.1.0"
8
8
  ---
9
9
 
10
10
  # morph-machine
@@ -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.0.0"
7
+ cliVersion: "9.1.0"
8
8
  stacks:
9
9
  - dotnet
10
10
  - nextjs
@@ -2,9 +2,9 @@
2
2
  name: morph-plan
3
3
  description: Leva uma feature do pedido ao plano aprovado — roteia o tipo de trabalho, varre o ambiente, conduz a entrevista pela árvore de decisões, escreve 2-plan/plan.md, extrai checks.json com plan-check e apresenta o gate do plano.
4
4
  version: core
5
- when_to_use: Use quando uma feature, bug ou chore começar ("/morph-proposal {feature}", "vamos planejar X"), ou ao retomar uma feature cujo gate plan ainda não foi aprovado
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.0.0"
7
+ cliVersion: "9.1.0"
8
8
  ---
9
9
 
10
10
  # morph-plan
@@ -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.0.0"
7
+ cliVersion: "9.1.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.0.0"
7
+ cliVersion: "9.1.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.0.0"
7
+ cliVersion: "9.1.0"
8
8
  ---
9
9
 
10
10
  # morph-uiux
@@ -22,7 +22,9 @@ Entra depois do gate do plano, quando a `morph-plan` §6 decidiu que a feature c
22
22
 
23
23
  - WCAG AA e todos os estados de componente interativo (default/hover/focus/disabled/loading/error), não só o caminho feliz.
24
24
  - Reusa token e componente existentes antes de criar; token novo precisa de justificativa. Sistemas, não telas isoladas.
25
- - **Toda tela sai com o checklist de tela preenchido.** O `advance --design` já escreveu `1-design/mockups.md` a partir do template do pacote, a fonte única dos dez itens (tabela `### Checklist de tela` dentro de cada `## Tela:`). "Não se aplica" vale se escrito; `…`, vazio ou "a definir" reprovam. Design sem tela nova ou alterada (só componente ou estilo): apague o `1-design/mockups.md` semeado. É o checklist que faz a lista de 2 000 linhas paginar: o verificador do Gate 3 cobra cada item decidido em `## Checklist de tela`. Tela de dados segue os standards de `admin-app` (lista/tabela, formulário, feedback, estrutura de página).
25
+ - **Toda tela sai com o checklist de tela preenchido.** O `advance --design` já escreveu `1-design/mockups.md` a partir do template do pacote, a fonte única dos dez itens (tabela `### Checklist de tela` dentro de cada `## Tela:`). "Não se aplica" vale se escrito; `…`, vazio ou "a definir" reprovam. Design sem tela nova ou alterada (só componente ou estilo): apague o `1-design/mockups.md` semeado. É o checklist que faz a lista de 2 000 linhas paginar: o verificador do Gate 3 cobra cada item decidido em `## Checklist de tela`. Tela de dados segue os standards de `admin-app` (lista/tabela, formulário, feedback, estrutura de página); login, onboarding, primeira tela, estado vazio de destaque e sucesso são `brand-moment`.
26
+ - **Conformidade não basta: a régua estética vale junto.** Direção escrita antes de componente (`1-design/direcao.md`: conceito, metáfora, o que torna a tela memorável, o que ela não pode parecer; 2 ou 3 direções para tela nova ou momento de marca), celular desenhado e não derivado, movimento com versão estática digna. A régua é o standard `frontend-design-system-art-direction`.
27
+ - **Toda tela nova ou alterada tem protótipo renderizado e conferido por print.** `1-design/prototipos/{tela}.html` autossuficiente, com os tokens reais do projeto (lidos do CSS de tokens), desktop e celular lado a lado, controles animado/estático/pausar; prints `{tela}-1280.png` e `{tela}-390.png` ao lado, saídos do laço de autocrítica. A `## Tela:` aponta o protótipo na linha `**Protótipo:**`.
26
28
 
27
29
  ## 3. Dispatch
28
30
 
@@ -34,17 +36,30 @@ Plano: `2-plan/plan.md` — fatias de UI [F2 (admin-app), F3 …]. Stack: [proje
34
36
  Design system: `.morph/context/design-system.md` (tokens e componentes a reusar).
35
37
 
36
38
  Nesta ordem, só o aplicável ao escopo:
37
- 1. `1-design/design-system.md` (obrigatório: sem ele o `advance` recusa) — tokens (cor, tipografia, espaço, motion) que a feature usa ou acrescenta, consistentes com o projeto; token novo também reescreve `.morph/context/design-system.md` (§4).
38
- 2. `mockups.md` — um `## Tela: <nome>` por tela nova ou alterada, com a fatia que a entrega
39
- e a tabela `### Checklist de tela` que o `advance --design` já escreveu em `1-design/mockups.md`,
40
- cada item decidido. Imagem via MCP stitch ou o de mídia quando houver; senão, descrição detalhada.
41
- 3. `components.md` — componentes novos e reusados, com props e estados.
42
- 4. `flows.md` — navegação entre as telas.
39
+ 1. `1-design/direcao.md` — antes de qualquer componente: conceito em uma frase, metáfora visual,
40
+ o que torna a tela memorável, o que ela NÃO pode parecer. Tela nova ou momento de marca:
41
+ 2 ou 3 direções distintas, uma recomendada com o porquê (standard `frontend-design-system-art-direction`).
42
+ 2. `1-design/design-system.md` (obrigatório: sem ele o `advance` recusa) — tokens (cor, tipografia, espaço, motion) que a feature usa ou acrescenta, lidos do CSS de tokens real do projeto; token novo também reescreve `.morph/context/design-system.md` (§4).
43
+ 3. `1-design/prototipos/{tela}.html` por tela nova ou alterada — autossuficiente, tokens reais,
44
+ desktop e celular (folha inferior, zona do polegar, 44px) lado a lado em moldura de aparelho,
45
+ controles animado/estático/pausar e `prefers-reduced-motion`. Laço de autocrítica: sirva a pasta
46
+ por HTTP local (o Playwright MCP recusa `file:`), print em 1280 e em 390 (`{tela}-1280.png`,
47
+ `{tela}-390.png` ao lado do HTML), avalie (hierarquia, contraste, ritmo, celular, "parece template?"),
48
+ corrija e repita até passar.
49
+ 4. `mockups.md` — um `## Tela: <nome>` por tela nova ou alterada, com a fatia que a entrega, a linha
50
+ `**Protótipo:** 1-design/prototipos/{tela}.html` e a tabela `### Checklist de tela` que o
51
+ `advance --design` já escreveu em `1-design/mockups.md`, cada item decidido.
52
+ 5. `components.md` — componentes novos e reusados, com props e estados.
53
+ 6. `flows.md` — navegação entre as telas.
43
54
 
44
55
  Relatório por artefato em `1-design/reports/[artefato]-report.md`: o que foi feito (2-3 frases),
45
- o artefato, as decisões de design e por quê, o que ficou aberto. É o retorno; a mensagem final é cortesia.
46
- Bash só para consulta (`morph-spec standards --find`, `npx`, screenshot, `git log/diff`): sem build,
47
- teste, `verify`, `approve` ou `advance`. Não toque `feature.json`, `state.json`, `plan.md` nem `checks.json`.
56
+ o artefato, as decisões de design e por quê, o que ficou aberto; o do protótipo lista as rodadas
57
+ do laço e o que cada uma corrigiu. É o retorno; a mensagem final é cortesia.
58
+ Bash só para consulta (`morph-spec standards --find`, `npx`, screenshot, `git log/diff`) e para o
59
+ servidor estático do protótipo (`npx http-server <pasta> -p <porta>` em background; ao fim, encerre
60
+ esse processo pelo id do background ou pelo PID dono da porta, nunca por nome de executável):
61
+ sem build, teste, `verify`, `approve` ou `advance`.
62
+ Não toque `feature.json`, `state.json`, `plan.md` nem `checks.json`.
48
63
  ```
49
64
 
50
65
  Ao retomar, **leia primeiro** `1-design/reports/*-report.md`. `design-system.md` é o único output obrigatório: sem ele, o `advance` recusa.
@@ -69,7 +84,9 @@ Cada ocorrência **nunca fica sem destino**: a fatia do plano que já a cobre (c
69
84
  morph-spec gate-check {feature} uiux [--json]
70
85
  ```
71
86
 
72
- O checklist é cobrado por código: tela sem a tabela, item ausente ou não decidido, ou nenhuma `## Tela:` **pausa** o gate nomeando tela e item, e `approve`/`advance` com assinatura `auto` recusam pela mesma regra (só o mockup antigo, telas `## S001:`, é isento). Complete o mockup antes de apresentar: não peça aprovação de um design que ainda não decidiu se a lista pagina. Divergência de design system vista aqui: `--signals '{"designSystemDivergence":true}'`.
87
+ O checklist é cobrado por código: tela sem a tabela, item ausente ou não decidido, ou nenhuma `## Tela:` **pausa** o gate nomeando tela e item, e `approve`/`advance` com assinatura `auto` recusam pela mesma regra (só o mockup antigo, telas `## S001:`, é isento). Com o checklist completo, cada tela ainda deve o protótipo: sem a linha `**Protótipo:**`, o HTML ou um dos prints de 1280/390, o gate **pausa** nomeando a tela e o arquivo. Complete o mockup antes de apresentar: não peça aprovação de um design que ainda não decidiu se a lista pagina, nem de um que ninguém renderizou.
88
+
89
+ **Apresente o design aberto, não descrito.** Antes da pergunta do gate, abra cada protótipo no navegador do usuário (sirva `1-design/prototipos/` por HTTP local e abra a URL: `start` no Windows, `open` no macOS, `xdg-open` no Linux) e cite a direção escolhida de `direcao.md` em uma frase. Divergência de design system vista aqui: `--signals '{"designSystemDivergence":true}'`.
73
90
 
74
91
  **`pause === true`**: `AskUserQuestion`, header "Gate uiux":
75
92
 
@@ -628,7 +628,7 @@
628
628
  "tool": "#vectorsearchtool-tool-maf",
629
629
  "multi-tenant": "#multi-tenant"
630
630
  },
631
- "digest": "Custom RAG on pgvector/Neon you control: chunking, embedding model via the Model Registry `embeddings` alias with `IEmbeddingGenerator<string, Embedding<float>>` injected directly into the ingestion handler and the VectorSearchTool (no EmbeddingService wrapper; batch GenerateAsync at ingestion, GenerateVectorAsync per query), the tool querying through EF Core (`CosineDistance` LINQ, raw NpgsqlDataSource only with a decisions.md reason), ids app-assigned (deterministic V5 for delta chunks, else Guid.CreateVersion7(); no gen_random_uuid() default), HNSW with vector_cosine_ops, tenant_id filtered in every query and never exposed to the LLM. Owns DELTA INGESTION by lookup-key: a pure Plan(incoming, lookupKey, contentHash, existingHashes) returning Added/Updated/Removed/Unchanged, where Unchanged is never re-embedded, Updated is an upsert on a deterministic id, Removed is applied AFTER the writes (no empty window), and a duplicate lookup-key fails the run naming both positions. Microsoft.Extensions.DataIngestion (still preview) solves hygiene, not embedding cost — its IncrementalIngestion deletes all pre-existing chunks of a document before rewriting. Open item: similarity threshold and k per scope."
631
+ "digest": "Custom RAG on pgvector/Neon you control: chunking, embedding model via the Model Registry `embeddings` alias with `IEmbeddingGenerator<string, Embedding<float>>` injected directly into the ingestion handler and the VectorSearchTool (no EmbeddingService wrapper; batch GenerateAsync at ingestion, GenerateVectorAsync per query), the tool querying through EF Core (`CosineDistance` LINQ, raw NpgsqlDataSource only with a plan.md (## Decisões) reason), ids app-assigned (deterministic V5 for delta chunks, else Guid.CreateVersion7(); no gen_random_uuid() default), HNSW with vector_cosine_ops, tenant_id filtered in every query and never exposed to the LLM. Owns DELTA INGESTION by lookup-key: a pure Plan(incoming, lookupKey, contentHash, existingHashes) returning Added/Updated/Removed/Unchanged, where Unchanged is never re-embedded, Updated is an upsert on a deterministic id, Removed is applied AFTER the writes (no empty window), and a duplicate lookup-key fails the run naming both positions. Microsoft.Extensions.DataIngestion (still preview) solves hygiene, not embedding cost — its IncrementalIngestion deletes all pre-existing chunks of a document before rewriting. Open item: similarity threshold and k per scope."
632
632
  },
633
633
  {
634
634
  "id": "ai-agents-service-tier-flex",
@@ -801,7 +801,7 @@
801
801
  "rules": "#padrões-obrigatórios",
802
802
  "cqrs": "#anti-patterns-nunca-fazer-em-vsa"
803
803
  },
804
- "digest": "Uma slice por operação em Features/{Entity}Feature/{Verb}{Entity}/: Request e Response (sealed records) no mesmo arquivo do Handler, Validator FluentValidation (nenhum em GetAll) e Endpoint `IApiEndpoint` `internal sealed`. O handler implementa o `IHandler<TRequest, TResponse>` próprio, devolve `Result<T>` (erros em `{Entity}Errors`, 1 por feature), injeta `ApplicationDbContext` direto e chama `SaveChangesAsync` nas escritas; IDs com `Guid.CreateVersion7()`, nunca `Guid.NewGuid()`; o endpoint usa `result.Match()`: o sucesso recebe o valor no lambda (nunca `result.Value`) e a falha vira `error.ToHttpResult()` (status pelo `ErrorType`, ProblemDetails), nunca `Results.BadRequest` para todo erro, declarando cada status com `.ProducesProblem`. Decorators Scrutor: Logging -> Validation -> Handler. PROIBIDO: MediatR/IRequest, AggregateRoot, DomainEvent, CQRS formal, Application Service layer, DTOs compartilhados entre features e `IRepository<T>`/`IUnitOfWork` (DbSet já é repository, vira código morto); store dedicado só com justificativa em `decisions.md`. Não misture VSA com DDD."
804
+ "digest": "Uma slice por operação em Features/{Entity}Feature/{Verb}{Entity}/: Request e Response (sealed records) no mesmo arquivo do Handler, Validator FluentValidation (nenhum em GetAll) e Endpoint `IApiEndpoint` `internal sealed`. O handler implementa o `IHandler<TRequest, TResponse>` próprio, devolve `Result<T>` (erros em `{Entity}Errors`, 1 por feature), injeta `ApplicationDbContext` direto e chama `SaveChangesAsync` nas escritas; IDs com `Guid.CreateVersion7()`, nunca `Guid.NewGuid()`; o endpoint usa `result.Match()`: o sucesso recebe o valor no lambda (nunca `result.Value`) e a falha vira `error.ToHttpResult()` (status pelo `ErrorType`, ProblemDetails), nunca `Results.BadRequest` para todo erro, declarando cada status com `.ProducesProblem`. Decorators Scrutor: Logging -> Validation -> Handler. PROIBIDO: MediatR/IRequest, AggregateRoot, DomainEvent, CQRS formal, Application Service layer, DTOs compartilhados entre features e `IRepository<T>`/`IUnitOfWork` (DbSet já é repository, vira código morto); store dedicado só com justificativa em `plan.md` (`## Decisões`). Não misture VSA com DDD."
805
805
  },
806
806
  {
807
807
  "id": "backend-api-validation",
@@ -821,7 +821,7 @@
821
821
  "rules": "#validation-patterns",
822
822
  "state": "#state-checks-in-the-handler"
823
823
  },
824
- "digest": "Request/DTO validation in the VSA slice: one sealed `AbstractValidator<TRequest>` (FluentValidation) next to the handler, checking SHAPE only (required, length, range, format, child items via RuleForEach/ChildRules; no DbContext; time rules read an injected TimeProvider inside the rule). The ValidationDecorator runs it before the handler and short-circuits with `Result.Failure(new ValidationError([...]))`; the endpoint maps it with `error.ToHttpResult()` to 400 ProblemDetails with `errors[]` (decorator and conversion in fluent-validation-vsa.md, ValidationError/ErrorHttpMapping in result-pattern.md). Never throw (ValidationException/InvalidOperationException), never validate in the endpoint lambda, never mix DataAnnotations with FluentValidation (minimal APIs have no ModelState). Existence/uniqueness/state checks live in the handler and return typed `{Entity}Errors` (NotFound 404, Conflict 409, Failure 400), never `Result.Failure(\"string\")`. State checks validate what is INVALID, not what is valid, so a new flow (e.g. a free flow that skips PendingPayment) does not break: ordered status enum (Created=0 ... EmailSent=4) with error states at 100+ (Failed=100, Cancelled=101, Refunded=102), blocked by `status >= Completed`, an error-state list, or `status >= Failed`. FORBIDDEN: `if (status != Expected)`, exhaustive switches over valid states, business rules mixed into the state check (keep `RequiresPayment && !IsPaid` separate). Document all flows in decisions.md and test each."
824
+ "digest": "Request/DTO validation in the VSA slice: one sealed `AbstractValidator<TRequest>` (FluentValidation) next to the handler, checking SHAPE only (required, length, range, format, child items via RuleForEach/ChildRules; no DbContext; time rules read an injected TimeProvider inside the rule). The ValidationDecorator runs it before the handler and short-circuits with `Result.Failure(new ValidationError([...]))`; the endpoint maps it with `error.ToHttpResult()` to 400 ProblemDetails with `errors[]` (decorator and conversion in fluent-validation-vsa.md, ValidationError/ErrorHttpMapping in result-pattern.md). Never throw (ValidationException/InvalidOperationException), never validate in the endpoint lambda, never mix DataAnnotations with FluentValidation (minimal APIs have no ModelState). Existence/uniqueness/state checks live in the handler and return typed `{Entity}Errors` (NotFound 404, Conflict 409, Failure 400), never `Result.Failure(\"string\")`. State checks validate what is INVALID, not what is valid, so a new flow (e.g. a free flow that skips PendingPayment) does not break: ordered status enum (Created=0 ... EmailSent=4) with error states at 100+ (Failed=100, Cancelled=101, Refunded=102), blocked by `status >= Completed`, an error-state list, or `status >= Failed`. FORBIDDEN: `if (status != Expected)`, exhaustive switches over valid states, business rules mixed into the state check (keep `RequiresPayment && !IsPaid` separate). Document all flows in plan.md (## Decisões) and test each."
825
825
  },
826
826
  {
827
827
  "id": "backend-database-postgresql-database",
@@ -988,7 +988,7 @@
988
988
  "endpoint": "#complete-endpoint-example",
989
989
  "registration": "#programcs-di-registration-order"
990
990
  },
991
- "digest": "Each VSA feature is a slice in `Features/{Feature}/{UseCase}/` with 5 files: Request, Response (sealed records, may be inline), Handler, Validator, Endpoint; feature errors in `{Feature}Errors.cs` (`Error.NotFound/Conflict/Failure`). Handlers implement `IHandler<TRequest, TResponse>` (resolved from DI; NOT MediatR, decorators via Scrutor), are `sealed class` with primary constructors, inject `ApplicationDbContext` directly (`DbSet<T>` is the repository, `DbContext` the unit of work: no service layer, no `IRepository<T>`/`IUnitOfWork`), use `Guid.CreateVersion7()` (never `Guid.NewGuid()`), pass `CancellationToken` everywhere and return `Result<T>` instead of throwing for expected failures. Endpoints are `internal sealed` `IApiEndpoint`, only map HTTP and call `Match()`: success lambda receives the value (never read `.Value`), failure is `error.ToHttpResult()` (status by `ErrorType`, never `Results.BadRequest` for every error), with `.ProducesProblem` per failure status. Raw SQL or a dedicated hand-written store (outbox, lease) only with a `decisions.md` justification."
991
+ "digest": "Each VSA feature is a slice in `Features/{Feature}/{UseCase}/` with 5 files: Request, Response (sealed records, may be inline), Handler, Validator, Endpoint; feature errors in `{Feature}Errors.cs` (`Error.NotFound/Conflict/Failure`). Handlers implement `IHandler<TRequest, TResponse>` (resolved from DI; NOT MediatR, decorators via Scrutor), are `sealed class` with primary constructors, inject `ApplicationDbContext` directly (`DbSet<T>` is the repository, `DbContext` the unit of work: no service layer, no `IRepository<T>`/`IUnitOfWork`), use `Guid.CreateVersion7()` (never `Guid.NewGuid()`), pass `CancellationToken` everywhere and return `Result<T>` instead of throwing for expected failures. Endpoints are `internal sealed` `IApiEndpoint`, only map HTTP and call `Match()`: success lambda receives the value (never read `.Value`), failure is `error.ToHttpResult()` (status by `ErrorType`, never `Results.BadRequest` for every error), with `.ProducesProblem` per failure status. Raw SQL or a dedicated hand-written store (outbox, lease) only with a `plan.md` (`## Decisões`) justification."
992
992
  },
993
993
  {
994
994
  "id": "backend-integrations-asaas-asaas-api",
@@ -1028,7 +1028,7 @@
1028
1028
  "jobs": "#job-types",
1029
1029
  "retry": "#retry--error-handling"
1030
1030
  },
1031
- "digest": "Hangfire 1.8.25 on .NET 10, only with a real reason (survive restarts, schedules, deferred retries) recorded in decisions.md; work that fits in the request stays in the slice handler. Storage is Neon Postgres via Hangfire.PostgreSql 1.21.1 with `UsePostgreSqlStorage(o => o.UseNpgsqlConnection(cs))` (the string overloads are [Obsolete]) and `HangfireConnection` on the direct/unpooled endpoint (schema install at startup is a migration, which Neon routes to the direct string; EnableLongPolling uses LISTEN/NOTIFY, unsupported by Neon's PgBouncer transaction mode); reference Newtonsoft.Json 13.0.4 explicitly or restore fails with NU1903 (Hangfire.Core pulls 11.0.1). A job is a thin sealed class in the slice that calls the slice handler (`IHandler<TRequest, Result>`); no XService layer. Hangfire.AspNetCore's AspNetCoreJobActivator opens a DI scope per job, so inject the ApplicationDbContext/handler directly (IDbContextFactory only for parallel work inside one job); never capture request-scoped services into Enqueue. Jobs are idempotent (check state, return). Expected failures come back as Result and the job logs and completes; unexpected ones are the exceptions EF/HttpClient already throw, uncaught, which `[AutomaticRetry(Attempts = 3, DelaysInSeconds = [60, 300, 900])]` retries; no try/catch, no manual retry loops. API: `BackgroundJob.Enqueue<TJob>(j => j.RunAsync(id, CancellationToken.None))`, `Schedule`, `RecurringJob.AddOrUpdate` with a stable id and cron (UTC), `ContinueJobWith`. Dashboard: IDashboardAuthorizationFilter requiring authenticated Admin, dev bypass only via IHostEnvironment.IsDevelopment() (never Request.Host, which the client controls)."
1031
+ "digest": "Hangfire 1.8.25 on .NET 10, only with a real reason (survive restarts, schedules, deferred retries) recorded in plan.md (## Decisões); work that fits in the request stays in the slice handler. Storage is Neon Postgres via Hangfire.PostgreSql 1.21.1 with `UsePostgreSqlStorage(o => o.UseNpgsqlConnection(cs))` (the string overloads are [Obsolete]) and `HangfireConnection` on the direct/unpooled endpoint (schema install at startup is a migration, which Neon routes to the direct string; EnableLongPolling uses LISTEN/NOTIFY, unsupported by Neon's PgBouncer transaction mode); reference Newtonsoft.Json 13.0.4 explicitly or restore fails with NU1903 (Hangfire.Core pulls 11.0.1). A job is a thin sealed class in the slice that calls the slice handler (`IHandler<TRequest, Result>`); no XService layer. Hangfire.AspNetCore's AspNetCoreJobActivator opens a DI scope per job, so inject the ApplicationDbContext/handler directly (IDbContextFactory only for parallel work inside one job); never capture request-scoped services into Enqueue. Jobs are idempotent (check state, return). Expected failures come back as Result and the job logs and completes; unexpected ones are the exceptions EF/HttpClient already throw, uncaught, which `[AutomaticRetry(Attempts = 3, DelaysInSeconds = [60, 300, 900])]` retries; no try/catch, no manual retry loops. API: `BackgroundJob.Enqueue<TJob>(j => j.RunAsync(id, CancellationToken.None))`, `Schedule`, `RecurringJob.AddOrUpdate` with a stable id and cron (UTC), `ContinueJobWith`. Dashboard: IDashboardAuthorizationFilter requiring authenticated Admin, dev bypass only via IHostEnvironment.IsDevelopment() (never Request.Host, which the client controls)."
1032
1032
  },
1033
1033
  {
1034
1034
  "id": "backend-integrations-neon-auth-neon-auth",
@@ -1055,7 +1055,7 @@
1055
1055
  "rls": "#row-level-security-integration",
1056
1056
  "postmortem": "#known-failure-modes-postmortem"
1057
1057
  },
1058
- "digest": "Use @neondatabase/auth SDK for Next.js. Auth data lives in neon_auth schema — no external provider, no webhooks. Server: createNeonAuth() with NEON_AUTH_BASE_URL + NEON_AUTH_COOKIE_SECRET. Client: createAuthClient(). Pages: AuthView component. Protection: auth.middleware() with runtime='nodejs' (Edge times out minting the session cookie). Register every dev/preview origin in trusted_origins before testing login. Two named prefix constants (BACKEND_API_PREFIX / PROXY_API_PREFIX) — never share one between server-direct and client-proxy calls. Frontend never queries Neon directly for business data by default — only its own auth/session SDK; a Data API bypass requires a decisions.md ADR. .NET: tokens are EdDSA/Ed25519 (OKP) — Microsoft.IdentityModel.Tokens can't verify OKP keys and there's no OIDC discovery; fetch+cache the JWKS yourself and verify signatures manually via BouncyCastle.Cryptography + TokenValidationParameters.SignatureValidator (must return Microsoft.IdentityModel.JsonWebTokens.JsonWebToken, not JwtSecurityToken — else IDX10506). NeonAuth:BaseUrl is mandatory in appsettings.Development.json (public value). RLS: auth.user_id() via pg_session_jwt."
1058
+ "digest": "Use @neondatabase/auth SDK for Next.js. Auth data lives in neon_auth schema — no external provider, no webhooks. Server: createNeonAuth() with NEON_AUTH_BASE_URL + NEON_AUTH_COOKIE_SECRET. Client: createAuthClient(). Pages: AuthView component. Protection: auth.middleware() with runtime='nodejs' (Edge times out minting the session cookie). Register every dev/preview origin in trusted_origins before testing login. Two named prefix constants (BACKEND_API_PREFIX / PROXY_API_PREFIX) — never share one between server-direct and client-proxy calls. Frontend never queries Neon directly for business data by default — only its own auth/session SDK; a Data API bypass requires a plan.md (## Decisões) ADR. .NET: tokens are EdDSA/Ed25519 (OKP) — Microsoft.IdentityModel.Tokens can't verify OKP keys and there's no OIDC discovery; fetch+cache the JWKS yourself and verify signatures manually via BouncyCastle.Cryptography + TokenValidationParameters.SignatureValidator (must return Microsoft.IdentityModel.JsonWebTokens.JsonWebToken, not JwtSecurityToken — else IDX10506). NeonAuth:BaseUrl is mandatory in appsettings.Development.json (public value). RLS: auth.user_id() via pg_session_jwt."
1059
1059
  },
1060
1060
  {
1061
1061
  "id": "backend-integrations-resend-resend-email",
@@ -1096,7 +1096,7 @@
1096
1096
  "patterns": "#cache-aside-pattern",
1097
1097
  "invalidation": "#ttl-strategy"
1098
1098
  },
1099
- "digest": "Redis (on the Coolify VPS or managed) is a distributed cache for sessions, rate limiting and hot reads, never the primary store. Packages: Microsoft.Extensions.Caching.StackExchangeRedis 10.0.12 (depends on StackExchange.Redis >= 2.7.27); reference StackExchange.Redis explicitly at the latest 2.x (2.13.17) only when injecting IConnectionMultiplexer -- 3.x is a new major the caching package has not adopted. Register one shared IConnectionMultiplexer as a LAZY singleton and hand it to the cache via AddOptions<RedisCacheOptions>().Configure<IServiceProvider>(... ConnectionMultiplexerFactory ...) -- never Connect during registration (an unreachable Redis would block boot) and never BuildServiceProvider inside the factory, InstanceName prefix (e.g. 'myapp:'), connection string from configuration (user-secrets / Coolify env ConnectionStrings__Redis). Cache-aside lives in the slice handler with IDistributedCache next to the ApplicationDbContext (no CachedXService/repository wrapper): read the key, on a miss project the response DTO from the DbContext, return Result NotFound (never null) when absent, write with DistributedCacheEntryOptions; the writing handler invalidates the exact key with RemoveAsync after SaveChangesAsync; key builders in one static class per feature. TTLs: user session 30 min sliding, product catalog 15 min absolute, rate-limit counters 1 min, computed aggregates 1 h, reference data 24 h. Rate limit across instances: StringIncrementAsync, KeyExpireAsync 1 min when count == 1, minute from TimeProvider (single instance: built-in AddRateLimiter). FORBIDDEN: caching passwords/tokens/PII without encryption, omitting TTL, swallowing RedisConnectionException in an empty catch (DB fallback during an outage is a decisions.md decision, implemented once at the cache boundary)."
1099
+ "digest": "Redis (on the Coolify VPS or managed) is a distributed cache for sessions, rate limiting and hot reads, never the primary store. Packages: Microsoft.Extensions.Caching.StackExchangeRedis 10.0.12 (depends on StackExchange.Redis >= 2.7.27); reference StackExchange.Redis explicitly at the latest 2.x (2.13.17) only when injecting IConnectionMultiplexer -- 3.x is a new major the caching package has not adopted. Register one shared IConnectionMultiplexer as a LAZY singleton and hand it to the cache via AddOptions<RedisCacheOptions>().Configure<IServiceProvider>(... ConnectionMultiplexerFactory ...) -- never Connect during registration (an unreachable Redis would block boot) and never BuildServiceProvider inside the factory, InstanceName prefix (e.g. 'myapp:'), connection string from configuration (user-secrets / Coolify env ConnectionStrings__Redis). Cache-aside lives in the slice handler with IDistributedCache next to the ApplicationDbContext (no CachedXService/repository wrapper): read the key, on a miss project the response DTO from the DbContext, return Result NotFound (never null) when absent, write with DistributedCacheEntryOptions; the writing handler invalidates the exact key with RemoveAsync after SaveChangesAsync; key builders in one static class per feature. TTLs: user session 30 min sliding, product catalog 15 min absolute, rate-limit counters 1 min, computed aggregates 1 h, reference data 24 h. Rate limit across instances: StringIncrementAsync, KeyExpireAsync 1 min when count == 1, minute from TimeProvider (single instance: built-in AddRateLimiter). FORBIDDEN: caching passwords/tokens/PII without encryption, omitting TTL, swallowing RedisConnectionException in an empty catch (DB fallback during an outage is a plan.md (## Decisões) decision, implemented once at the cache boundary)."
1100
1100
  },
1101
1101
  {
1102
1102
  "id": "data-nosql-cosmos-db",
@@ -1116,7 +1116,7 @@
1116
1116
  "modeling": "#container-design",
1117
1117
  "queries": "#handler-in-the-slice"
1118
1118
  },
1119
- "digest": "OPTIONAL — outside the baked-in stack. The default store is Neon (Postgres) via EF Core; Cosmos needs a decisions.md entry saying why Neon does not serve (multi-region writes, event-stream throughput, per-tenant schema never joined) — never for joined data, small datasets or 'flexible schema' (jsonb covers it). Stack rules still hold: one singleton CosmosClient with System.Text.Json, the Container as a keyed singleton injected straight into the slice handler (no CosmosRepository<T>), ids from Guid.CreateVersion7(), time from TimeProvider. Use ReadItemStreamAsync/CreateItemStreamAsync and map StatusCode 404/409 to Result errors instead of catching CosmosException. Parameterized queries with the PartitionKey pinned in QueryRequestOptions (no fan-out). Microsoft.Azure.Cosmos 3.x needs an explicit Newtonsoft.Json reference or the build fails."
1119
+ "digest": "OPTIONAL — outside the baked-in stack. The default store is Neon (Postgres) via EF Core; Cosmos needs a plan.md (## Decisões) entry saying why Neon does not serve (multi-region writes, event-stream throughput, per-tenant schema never joined) — never for joined data, small datasets or 'flexible schema' (jsonb covers it). Stack rules still hold: one singleton CosmosClient with System.Text.Json, the Container as a keyed singleton injected straight into the slice handler (no CosmosRepository<T>), ids from Guid.CreateVersion7(), time from TimeProvider. Use ReadItemStreamAsync/CreateItemStreamAsync and map StatusCode 404/409 to Result errors instead of catching CosmosException. Parameterized queries with the PartitionKey pinned in QueryRequestOptions (no fan-out). Microsoft.Azure.Cosmos 3.x needs an explicit Newtonsoft.Json reference or the build fails."
1120
1120
  },
1121
1121
  {
1122
1122
  "id": "data-vector-search-rag-chunking",
@@ -1250,9 +1250,10 @@
1250
1250
  "atmosphere": "#section-2-atmosphere-never-flat-color",
1251
1251
  "loadin": "#section-3-one-orchestrated-load-in-beats-ten-micro-interactions",
1252
1252
  "legibility": "#section-4-legibility-over-video-close-to-a-hard-rule",
1253
- "order": "#section-7-order-of-application"
1253
+ "order": "#section-7-order-of-application",
1254
+ "apppatterns": "#section-8-patterns-for-app-screens"
1254
1255
  },
1255
- "digest": "The finish layer that separates a working build from one a client reads as expensive, applied from the start rather than as a final polish pass. Principles, never a fixed recipe, and never converge on the same choices across projects: commit to a display font chosen from the brand (stack defaults and the AI-tell faces like Space Grotesk/Playfair announce machine-generated); use atmosphere instead of flat color (feTurbulence film grain at low opacity, radial brand glow, vignette); concentrate effort on ONE orchestrated hero load-in with staggered animation-delay rather than scattered micro-interactions; text over media always gets a DIRECTIONAL scrim (dark where the copy lives, clear over the subject) plus text-shadow as insurance against bright frames; one precious accent used in three places or fewer. Define brand tokens as CSS variables first, then atmosphere, then hero, then accent, then details."
1256
+ "digest": "The finish layer that separates a working build from one a client reads as expensive, applied from the start rather than as a final polish pass. Principles, never a fixed recipe, and never converge on the same choices across projects: commit to a display font chosen from the brand (stack defaults and the AI-tell faces like Space Grotesk/Playfair announce machine-generated); use atmosphere instead of flat color (feTurbulence film grain at low opacity, radial brand glow, vignette); concentrate effort on ONE orchestrated hero load-in with staggered animation-delay rather than scattered micro-interactions; text over media always gets a DIRECTIONAL scrim (dark where the copy lives, clear over the subject) plus text-shadow as insurance against bright frames; one precious accent used in three places or fewer. Define brand tokens as CSS variables first, then atmosphere, then hero, then accent, then details. App screens too (every ui-designer surface): bottom sheet on mobile with 44px targets, stories-style progress and synchronized highlight sharing --i/--beat via animation-delay, scene cards without real data (never a made-up number), dot grid faded with mask-image, and a composed static frame for prefers-reduced-motion."
1256
1257
  },
1257
1258
  {
1258
1259
  "id": "frontend-gsap-core",
@@ -1689,7 +1690,7 @@
1689
1690
  "setup": "#enable-pgvector",
1690
1691
  "queries": "#similarity-search"
1691
1692
  },
1692
- "digest": "pgvector on Neon with .NET VSA: packages Npgsql.EntityFrameworkCore.PostgreSQL 10.0.3 + Pgvector.EntityFrameworkCore 0.3.0 (snake_case columns mapped with HasColumnName, no naming-convention package); HasPostgresExtension('vector') in OnModelCreating emits CREATE EXTENSION in the migration. Map the column as Pgvector `Vector` (`vector(1536)` for text-embedding-3-small; EF does not map float[] or ReadOnlyMemory<float>) and call `npgsql.UseVector()` inside UseNpgsql (NpgsqlDataSourceBuilder.UseVector only for raw Npgsql). ALWAYS an HNSW index (HasMethod('hnsw'), vector_cosine_ops, m 16, ef_construction 64); the operator class must match the LINQ function (CosineDistance/L2Distance/MaxInnerProduct) or Postgres seq-scans. Query in the slice handler through ApplicationDbContext: `d.Embedding!.CosineDistance(query)`, filter by distance <= 1 - minSimilarity, OrderBy distance ASC (similarity = 1 - distance, never ORDER BY similarity DESC), return Result; no DocumentRepository, no raw SQL from C# (SQL functions such as match_documents/hybrid search only with a decisions.md justification). Ids from Guid.CreateVersion7() in the handler, never gen_random_uuid() defaults. ef_search (default 40) on the pooled endpoint needs SET LOCAL inside the query's transaction."
1693
+ "digest": "pgvector on Neon with .NET VSA: packages Npgsql.EntityFrameworkCore.PostgreSQL 10.0.3 + Pgvector.EntityFrameworkCore 0.3.0 (snake_case columns mapped with HasColumnName, no naming-convention package); HasPostgresExtension('vector') in OnModelCreating emits CREATE EXTENSION in the migration. Map the column as Pgvector `Vector` (`vector(1536)` for text-embedding-3-small; EF does not map float[] or ReadOnlyMemory<float>) and call `npgsql.UseVector()` inside UseNpgsql (NpgsqlDataSourceBuilder.UseVector only for raw Npgsql). ALWAYS an HNSW index (HasMethod('hnsw'), vector_cosine_ops, m 16, ef_construction 64); the operator class must match the LINQ function (CosineDistance/L2Distance/MaxInnerProduct) or Postgres seq-scans. Query in the slice handler through ApplicationDbContext: `d.Embedding!.CosineDistance(query)`, filter by distance <= 1 - minSimilarity, OrderBy distance ASC (similarity = 1 - distance, never ORDER BY similarity DESC), return Result; no DocumentRepository, no raw SQL from C# (SQL functions such as match_documents/hybrid search only with a plan.md (## Decisões) justification). Ids from Guid.CreateVersion7() in the handler, never gen_random_uuid() defaults. ef_search (default 40) on the pooled endpoint needs SET LOCAL inside the query's transaction."
1693
1694
  },
1694
1695
  {
1695
1696
  "id": "infrastructure-neon-setup",
@@ -1829,7 +1830,7 @@
1829
1830
  "not-worth": "#o-que-não-merece",
1830
1831
  "size": "#tamanho"
1831
1832
  },
1832
- "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 decisions.md; 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."
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."
1833
1834
  },
1834
1835
  {
1835
1836
  "id": "ai-agents-tool-adapters",
@@ -1857,6 +1858,27 @@
1857
1858
  "regras": "#regras"
1858
1859
  },
1859
1860
  "digest": "A tool that carries business rule keeps apart three things that change for different reasons: the RULE (sealed class XOps(...), primary constructor, explicit AddScoped; never *Service, which MORPH.md bans as a layer between handler and data), the sentence the MODEL reads, and the EFFECT a human sees. Each Ops method returns a typed outcome (abstract record XDesfecho, one sealed record per case); the model-facing text (description, refusal, self-correction, success, the ToolUnavailable decision) lives in the adapter, in public static object Render(XDesfecho), while human text delivered by code (task body, note, ack) stays in Ops because it is an effect. Checkable by grep: Ops names no tool and instructs no model. The adapter (AIFunctionFactory.Create) only builds the typed request, calls Ops and returns Render: zero rule, not a line count, and a long dynamic description stays byte for byte. Two test layers: Ops tests assert outcome, calls and order on the fake, never text; contract tests assert Render's exact literal and .Description, with a golden of name, description and schema per real tenant. A neutral refactor is proven before any test moves: a proof commit with the old suite untouched and green (offline evals included), then the migration, with mutation run only against the new tests. \"Already covered\" needs proof assertion by assertion (11 of 22 such claims had lost one); the tests left at the origin must be identical to the base; mutation on the new tests found rules the old suite never protected. The C# blocks were compiled against the pin and the three xunit tests run."
1861
+ },
1862
+ {
1863
+ "id": "frontend-design-system-art-direction",
1864
+ "name": "art direction",
1865
+ "path": "frontend/design-system/art-direction.md",
1866
+ "category": "frontend",
1867
+ "subcategory": "design-system",
1868
+ "tags": [
1869
+ "frontend",
1870
+ "design-system"
1871
+ ],
1872
+ "anchors": {
1873
+ "direction": "#section-1-write-the-direction-first",
1874
+ "explore": "#section-2-explore-two-or-three-directions",
1875
+ "examples": "#section-3-good-and-bad-directions",
1876
+ "tells": "#section-4-template-tells-to-avoid",
1877
+ "mobile": "#section-5-mobile-is-designed-not-derived",
1878
+ "motion": "#section-6-motion-with-intent",
1879
+ "prototype": "#section-7-prototype-and-self-review"
1880
+ },
1881
+ "digest": "Order beats talent: before any component of a new or changed screen, write 1-design/direcao.md with the concept in one sentence, the visual metaphor, what makes it memorable, and what it must NOT look like. New screens and brand moments (login, onboarding, first screen, empty state, success) explore 2-3 DISTINCT directions and recommend one with a product reason. Avoid template tells (raw shadcn look, white card centered on gray, generic icon list, default fonts, flat backgrounds, mobile as stacked desktop, decorative fade-ins, made-up numbers); three or more means raise it. Mobile is composed at 390px on its own (bottom sheet, thumb zone, 44px targets). Motion tells something with the lightest tool (CSS keyframes > scroll-driven CSS > Framer Motion > GSAP), transform/opacity only, and a composed reduced-motion version. Prove it in 1-design/prototipos/{screen}.html with the project REAL tokens, screenshotted at 1280 and 390 and revised until it passes."
1860
1882
  }
1861
1883
  ]
1862
1884
  }
@@ -406,7 +406,7 @@
406
406
  "spec publicado",
407
407
  "reconciliação de prompt"
408
408
  ],
409
- "verifiedAgainst": "Microsoft.Agents.AI 1.20.0 + Microsoft.Extensions.AI 10.9.0 (ai-pin 2026-09-08). **Verificação documental**, sem cláusula `provado por` — o ai-kit não traz loader de prompt, e os dois mecanismos descritos aqui são destilados de três produtos em produção. As **quatro** superfícies de API que este standard cita foram medidas por reflexão, e vivem em **três assemblies diferentes** — o carimbo diz onde remedir, então o nome importa: `IChatClient.AsAIAgent(instructions:, name:)` é `Microsoft.Extensions.AI.ChatClientExtensions`, em **`Microsoft.Agents.AI` 1.20.0** (medida em 2026-09-08); `AIAgent.RunAsync` (as oito sobrecargas) e `AIAgent.CreateSessionAsync` são de `Microsoft.Agents.AI.AIAgent`, na assembly **`Microsoft.Agents.AI.Abstractions` 1.20.0** — **remedidas** em 2026-09-09, porque a leitura de 2026-09-08 estava errada no *quê*, e a de 2026-09-09 estava errada no *onde* (as duas retificações estão no callout abaixo); e `builder.AddAIAgent` (§Keyed singleton) é `Microsoft.Agents.AI.Hosting.HostApplicationBuilderAgentExtensions` — cinco sobrecargas, na assembly **`Microsoft.Agents.AI.Hosting` 1.20.0.0**, que vem do pacote **`Microsoft.Agents.AI.Hosting` `1.20.0-preview.260831.1`**: ele é **prerelease** e está **FORA do `ai-pin.json`** (apurado em `scripts/api-probe/companions.json`, 2026-09-08) — quem adotar esta seção registra a dependência prerelease em `decisions.md`, como manda `ai-agents-production`. **Duas ressalvas de escopo**, porque um carimbo que não as diz promete mais do que mediu: `Microsoft.Agents.AI.Abstractions` e `Microsoft.Extensions.AI.Abstractions` são nomes de **assembly** (elas viajam dentro dos pacotes `Microsoft.Agents.AI` e `Microsoft.Extensions.AI`), não ids de pacote; e `IMemoryCache`/`DbContext`, usados no padrão de cache e na §Keyed singleton, são superfícies de ASP.NET Core e EF Core — **fora do pin, não medidas aqui, verificação documental**.",
409
+ "verifiedAgainst": "Microsoft.Agents.AI 1.20.0 + Microsoft.Extensions.AI 10.9.0 (ai-pin 2026-09-08). **Verificação documental**, sem cláusula `provado por` — o ai-kit não traz loader de prompt, e os dois mecanismos descritos aqui são destilados de três produtos em produção. As **quatro** superfícies de API que este standard cita foram medidas por reflexão, e vivem em **três assemblies diferentes** — o carimbo diz onde remedir, então o nome importa: `IChatClient.AsAIAgent(instructions:, name:)` é `Microsoft.Extensions.AI.ChatClientExtensions`, em **`Microsoft.Agents.AI` 1.20.0** (medida em 2026-09-08); `AIAgent.RunAsync` (as oito sobrecargas) e `AIAgent.CreateSessionAsync` são de `Microsoft.Agents.AI.AIAgent`, na assembly **`Microsoft.Agents.AI.Abstractions` 1.20.0** — **remedidas** em 2026-09-09, porque a leitura de 2026-09-08 estava errada no *quê*, e a de 2026-09-09 estava errada no *onde* (as duas retificações estão no callout abaixo); e `builder.AddAIAgent` (§Keyed singleton) é `Microsoft.Agents.AI.Hosting.HostApplicationBuilderAgentExtensions` — cinco sobrecargas, na assembly **`Microsoft.Agents.AI.Hosting` 1.20.0.0**, que vem do pacote **`Microsoft.Agents.AI.Hosting` `1.20.0-preview.260831.1`**: ele é **prerelease** e está **FORA do `ai-pin.json`** (apurado em `scripts/api-probe/companions.json`, 2026-09-08) — quem adotar esta seção registra a dependência prerelease em `plan.md` (`## Decisões`), como manda `ai-agents-production`. **Duas ressalvas de escopo**, porque um carimbo que não as diz promete mais do que mediu: `Microsoft.Agents.AI.Abstractions` e `Microsoft.Extensions.AI.Abstractions` são nomes de **assembly** (elas viajam dentro dos pacotes `Microsoft.Agents.AI` e `Microsoft.Extensions.AI`), não ids de pacote; e `IMemoryCache`/`DbContext`, usados no padrão de cache e na §Keyed singleton, são superfícies de ASP.NET Core e EF Core — **fora do pin, não medidas aqui, verificação documental**.",
410
410
  "provedBy": []
411
411
  },
412
412
  "ai-agents-providers-model-registry": {
@@ -842,7 +842,7 @@
842
842
  "metallic accent",
843
843
  "looks like a template"
844
844
  ],
845
- "verifiedAgainst": "Production landing pages, CSS-only techniques, no runtime dependency.",
845
+ "verifiedAgainst": "Production landing pages, CSS-only techniques, no runtime dependency; app patterns from an approved login prototype (2026-10-06).",
846
846
  "provedBy": []
847
847
  },
848
848
  "frontend-gsap-core": {
@@ -1209,6 +1209,20 @@
1209
1209
  ],
1210
1210
  "verifiedAgainst": "Microsoft.Extensions.AI 10.9.0 + Microsoft.Agents.AI 1.20.0 (ai-pin 2026-09-08); os dois blocos C# abaixo foram extraídos com `scripts/extract-doc-csharp.mjs` e compilados contra o pin com `TreatWarningsAsErrors`, e os três testes do segundo bloco foram executados e passam. **A separação em si e a migração de teste são verificação documental:** vêm da feature `tools-finas` do MORPH_GHLBrain e o ai-kit não as traz.",
1211
1211
  "provedBy": []
1212
+ },
1213
+ "frontend-design-system-art-direction": {
1214
+ "aliases": [
1215
+ "direção de arte",
1216
+ "direcao de arte",
1217
+ "conceito visual",
1218
+ "cara de template",
1219
+ "tela sem graça",
1220
+ "design genérico",
1221
+ "metáfora visual",
1222
+ "momento de marca"
1223
+ ],
1224
+ "verifiedAgainst": "the `login-ciclo-do-produto` design round (2026-10-06): a correct, plain design was rejected; a second round with a written direction, an HTML prototype and a screenshot self-review was approved on first sight.",
1225
+ "provedBy": []
1212
1226
  }
1213
1227
  },
1214
1228
  "sharedAliases": {
@@ -128,13 +128,13 @@ não escolhe o histórico; escolher "chat com histórico" não escolhe as fases.
128
128
  | Agente de batch no caminho síncrono de uma requisição | Latência de LLM no p99 do endpoint, sem retry e sem visibilidade | Job assíncrono |
129
129
  | Imagem descrita numa chamada e tipada noutra | Duas chamadas pagas, e o erro de descrição deixa de ser rastreável | Uma chamada multimodal com `RunAsync<T>` |
130
130
  | Projeção papel → `ChatMessage` em dois arquivos | Os dois divergem, e o bug aparece como "o agente esqueceu" | Uma projeção, pura e testável |
131
- | Escolher o arquétipo na implementação | A forma muda o desenho das tasks; escolhida depois, ela vira refactor | Escolha na fase de plano, e registre em `decisions.md` |
131
+ | Escolher o arquétipo na implementação | A forma muda o desenho das tasks; escolhida depois, ela vira refactor | Escolha na fase de plano, e registre em `plan.md` (`## Decisões`) |
132
132
 
133
133
  ---
134
134
 
135
135
  ## Checklist (verifiable in review)
136
136
 
137
- - [ ] O arquétipo escolhido está **nomeado** no `decisions.md` da feature, com a pergunta de
137
+ - [ ] O arquétipo escolhido está **nomeado** no `plan.md` (`## Decisões`) da feature, com a pergunta de
138
138
  `## Como escolher` que o decidiu
139
139
  - [ ] Nenhum agente novo por fase de conversa — a fase é dado do turno
140
140
  - [ ] Agente one-shot recebe prompt e alias como parâmetro, e não guarda estado entre invocações
@@ -228,7 +228,7 @@ O histórico cresce a cada turno. Quando estoura o context window do modelo, as
228
228
 
229
229
  ## Checklist (verifiable in review)
230
230
 
231
- - [ ] A escolha da fonte de histórico foi feita pela tabela de critério — e está escrita em `decisions.md` quando não foi a conversa própria
231
+ - [ ] A escolha da fonte de histórico foi feita pela tabela de critério — e está escrita em `plan.md` (`## Decisões`) quando não foi a conversa própria
232
232
  - [ ] O provider de histórico é registrado como **uma** instância compartilhada
233
233
  - [ ] Nenhum campo de instância do provider guarda estado de conversa; o que é por sessão está em `ProviderSessionState<TState>` e declarado em `StateKeys`
234
234
  - [ ] Existe teste com **duas sessões concorrentes** sobre o mesmo provider
@@ -170,7 +170,7 @@ comprar a única coisa que o aninhamento oferece — deixar o modelo decidir.
170
170
  contabilidade por agente); o teto de tool calls do externo não governa o interno; o timeout do turno
171
171
  passa a cobrir duas chamadas de LLM em série; e o span do interno pendura-se sob `execute_tool`, onde
172
172
  nenhum painel de agente procura. Quando a decisão **é** do modelo, esses quatro custos continuam
173
- valendo — a diferença é que aí eles foram escolhidos, e o `decisions.md` diz por quê.
173
+ valendo — a diferença é que aí eles foram escolhidos, e o `plan.md` (`## Decisões`) diz por quê.
174
174
 
175
175
  Os dois lados de campo, e é raro ter os dois:
176
176
 
@@ -198,7 +198,7 @@ A composição legítima — quando ela é mesmo necessária — é assunto de
198
198
  | Teto tratado como opção de chamada | Vai ao provider, onde não significa nada | É opção do pipeline, nível de agente |
199
199
  | `RequireAny` adotado sem medir `recovered` | Troca uma recuperação medida por uma falha nova | Aplique o critério de saída antes |
200
200
  | Etapa FIXA do turno aninhada numa tool call | Paga custo, teto, timeout e span quebrados sem comprar nada — ninguém precisava decidir | Sequência, no código |
201
- | Agent-as-Tool adotado sem registrar a decisão, ou além de 2 níveis | Os quatro custos viram surpresa, e o 3º nível é sinal de que devia ser sequência | Decisão no `decisions.md`, teto ≤ 2 níveis (`ai-agents-multi-agent-patterns`) |
201
+ | Agent-as-Tool adotado sem registrar a decisão, ou além de 2 níveis | Os quatro custos viram surpresa, e o 3º nível é sinal de que devia ser sequência | Decisão no `plan.md` (`## Decisões`), teto ≤ 2 níveis (`ai-agents-multi-agent-patterns`) |
202
202
 
203
203
  ---
204
204
 
@@ -212,8 +212,8 @@ A composição legítima — quando ela é mesmo necessária — é assunto de
212
212
  - [ ] Há teto de tool calls por turno, com tool isenta, e bater o teto é observável
213
213
  - [ ] O teto está no nível de agente, não nas opções de chamada
214
214
  - [ ] Se `RequireAny` foi adotado: a taxa de `recovered` e o custo da segunda chamada estão no
215
- `decisions.md`. Se não foi: o critério de saída está escrito
216
- - [ ] Nenhuma etapa **fixa** do turno é invocada de dentro de uma tool call. Onde há aninhamento, quem decide é o **modelo**, está registrado no `decisions.md`, e respeita o teto de ≤ 2 níveis de `ai-agents-multi-agent-patterns`
215
+ `plan.md` (`## Decisões`). Se não foi: o critério de saída está escrito
216
+ - [ ] Nenhuma etapa **fixa** do turno é invocada de dentro de uma tool call. Onde há aninhamento, quem decide é o **modelo**, está registrado no `plan.md` (`## Decisões`), e respeita o teto de ≤ 2 níveis de `ai-agents-multi-agent-patterns`
217
217
 
218
218
  ---
219
219