@polymorphism-tech/morph-spec 9.8.0 → 10.1.1

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 (202) hide show
  1. package/CLAUDE.md +12 -10
  2. package/README.md +4 -4
  3. package/bin/morph-spec.js +40 -43
  4. package/claude-plugin.json +1 -1
  5. package/docs/CHEATSHEET.md +28 -36
  6. package/docs/QUICKSTART.md +31 -191
  7. package/framework/CLAUDE.md +24 -30
  8. package/framework/MORPH.md +47 -123
  9. package/framework/agents.json +6 -374
  10. package/framework/commands/morph-apply.md +1 -1
  11. package/framework/commands/morph-archive.md +4 -3
  12. package/framework/commands/morph-hotfix.md +8 -30
  13. package/framework/commands/morph-infra.md +2 -2
  14. package/framework/commands/morph-media.md +10 -9
  15. package/framework/commands/morph-preflight.md +3 -3
  16. package/framework/commands/morph-review.md +15 -0
  17. package/framework/commands/morph-status.md +1 -1
  18. package/framework/docs/CLI.md +84 -95
  19. package/framework/evals/review-checklist.md +45 -53
  20. package/framework/hooks/README.md +4 -4
  21. package/framework/hooks/claude-code/core/README.md +1 -1
  22. package/framework/hooks/claude-code/core/denylist.json +4 -4
  23. package/framework/hooks/claude-code/core/gate-guard.js +9 -0
  24. package/framework/hooks/claude-code/core/masked-exit.js +49 -0
  25. package/framework/hooks/claude-code/core/state-sync.js +4 -1
  26. package/framework/hooks/claude-code/core/telemetry-log.js +1 -2
  27. package/framework/hooks/claude-code/core/test-budget.js +10 -70
  28. package/framework/hooks/claude-code/core/validate-completion.js +11 -24
  29. package/framework/hooks/claude-code/core/worktree-context.js +5 -22
  30. package/framework/hooks/claude-code/core/worktree-create.js +6 -45
  31. package/framework/hooks/claude-code/core/worktree-env-inject.js +2 -10
  32. package/framework/hooks/shared/e2e-check.js +0 -0
  33. package/framework/hooks/shared/gate-event.js +2 -2
  34. package/framework/hooks/shared/graph-refresh.js +20 -3
  35. package/framework/hooks/shared/heavy-command.js +32 -1
  36. package/framework/hooks/shared/heavy-config.js +1 -1
  37. package/framework/hooks/shared/next-step.js +63 -24
  38. package/framework/hooks/shared/phase-utils.js +6 -9
  39. package/framework/hooks/shared/report-progress.js +86 -0
  40. package/framework/hooks/shared/state-reader.js +3 -2
  41. package/framework/hooks/statusline.js +332 -0
  42. package/framework/mcp/registry.json +4 -4
  43. package/framework/phases.json +32 -10
  44. package/framework/rules/ai-agents-standards.md +10 -2
  45. package/framework/rules/csharp-standards.md +4 -0
  46. package/framework/rules/data-standards.md +1 -1
  47. package/framework/rules/ef-standards.md +20 -0
  48. package/framework/rules/frontend-standards.md +1 -1
  49. package/framework/rules/hangfire-standards.md +1 -1
  50. package/framework/rules/implement-report.md +25 -0
  51. package/framework/rules/infrastructure-standards.md +2 -0
  52. package/framework/rules/neon-auth-standards.md +1 -1
  53. package/framework/rules/neon-standards.md +3 -3
  54. package/framework/rules/nextjs-standards.md +7 -0
  55. package/framework/rules/plan-grammar.md +53 -0
  56. package/framework/rules/ui-design.md +27 -0
  57. package/framework/schemas/config.schema.json +11 -18
  58. package/framework/schemas/feature.schema.json +7 -1
  59. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  60. package/framework/skills/core/create-umbraco-lp/references/umbraco-lp-patterns.md +1 -1
  61. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +2 -2
  62. package/framework/skills/core/morph-apply/SKILL.md +39 -166
  63. package/framework/skills/core/morph-apply/references/debugging.md +8 -0
  64. package/framework/skills/core/morph-apply/references/hotfix.md +42 -0
  65. package/framework/skills/core/morph-apply/references/machine.md +29 -0
  66. package/framework/skills/core/{morph-media/SKILL.md → morph-apply/references/media.md} +11 -74
  67. package/framework/skills/core/morph-apply/references/review.md +65 -0
  68. package/framework/skills/core/morph-plan/SKILL.md +49 -109
  69. package/framework/skills/core/morph-plan/references/critique.md +43 -0
  70. package/framework/skills/core/morph-plan/references/interview.md +37 -0
  71. package/framework/skills/core/morph-uiux/SKILL.md +47 -64
  72. package/framework/skills/core/morph-uiux/surfaces.json +32 -0
  73. package/framework/standards/STANDARDS.json +51 -28
  74. package/framework/standards/STANDARDS.meta.json +16 -19
  75. package/framework/standards/ai-agents/agent-session.md +2 -3
  76. package/framework/standards/ai-agents/context-providers.md +2 -2
  77. package/framework/standards/ai-agents/durable-workflows-hitl.md +2 -2
  78. package/framework/standards/ai-agents/providers/model-registry.md +2 -2
  79. package/framework/standards/ai-agents/rag-custom-pgvector.md +7 -6
  80. package/framework/standards/ai-agents/setup.md +1 -1
  81. package/framework/standards/ai-agents/tool-adapters.md +2 -2
  82. package/framework/standards/architecture/stack-baseline.md +70 -0
  83. package/framework/standards/backend/database/postgresql/database.md +22 -1
  84. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +33 -1
  85. package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +2 -1
  86. package/framework/standards/backend/integrations/neon-auth/neon-auth.md +12 -13
  87. package/framework/standards/frontend/design-system/ai-image-generation.md +51 -12
  88. package/framework/standards/frontend/design-system/ai-video-generation.md +50 -4
  89. package/framework/standards/frontend/design-system/art-direction.md +2 -2
  90. package/framework/standards/frontend/scroll-driven/frame-scrub.md +12 -2
  91. package/framework/standards/frontend/scroll-driven/scroll-components.md +76 -4
  92. package/framework/standards/infrastructure/docker/local-compose-isolation.md +2 -2
  93. package/framework/standards/infrastructure/neon/neon-pgvector.md +62 -20
  94. package/framework/standards/infrastructure/neon/neon-platform.md +61 -0
  95. package/framework/standards/infrastructure/neon/neon-setup.md +14 -13
  96. package/framework/standards/integration/mcp/mcp-tools.md +20 -20
  97. package/framework/templates/REGISTRY.json +1 -2
  98. package/framework/templates/context/ARCHITECTURE.md +1 -1
  99. package/framework/templates/docs/plan.md +15 -12
  100. package/framework/templates/docs/ui-mockups.md +17 -105
  101. package/framework/templates/infrastructure/docker/docker-compose.template.yml +1 -1
  102. package/package.json +1 -1
  103. package/src/commands/e2e/e2e.js +88 -23
  104. package/src/commands/graph/diff.js +74 -0
  105. package/src/commands/graph/status.js +1 -1
  106. package/src/commands/observability/cost.js +2 -4
  107. package/src/commands/observability/telemetry.js +1 -1
  108. package/src/commands/project/doctor.js +23 -39
  109. package/src/commands/project/fleet.js +5 -6
  110. package/src/commands/project/init.js +63 -36
  111. package/src/commands/project/stack-scan.js +3 -42
  112. package/src/commands/project/stack-set.js +26 -0
  113. package/src/commands/project/status.js +12 -13
  114. package/src/commands/project/update.js +39 -3
  115. package/src/commands/project/worktree.js +3 -14
  116. package/src/commands/state/advance.js +17 -17
  117. package/src/commands/state/approve.js +0 -6
  118. package/src/commands/state/archive.js +27 -1
  119. package/src/commands/state/auto-approval.js +2 -2
  120. package/src/commands/state/create.js +1 -6
  121. package/src/commands/state/gate-check.js +38 -31
  122. package/src/commands/state/gate-decision.js +3 -2
  123. package/src/commands/state/next.js +1 -0
  124. package/src/commands/state/recap.js +67 -0
  125. package/src/commands/state/report-check.js +36 -178
  126. package/src/commands/validation/plan-check.js +16 -11
  127. package/src/commands/validation/validate-feature.js +3 -3
  128. package/src/commands/validation/verify.js +3 -6
  129. package/src/core/paths/output-schema.js +3 -3
  130. package/src/core/state/state-manager.js +2 -4
  131. package/src/dashboard/app/components/CommandPalette.jsx +2 -5
  132. package/src/dashboard/app/components/GatePauses.jsx +116 -0
  133. package/src/dashboard/app/components/SessionMap.jsx +77 -0
  134. package/src/dashboard/app/pages/Activity.jsx +4 -0
  135. package/src/dashboard/app/pages/AgentMap.jsx +1 -2
  136. package/src/dashboard/app/pages/FeatureDetail.jsx +129 -186
  137. package/src/dashboard/app/pages/Features.jsx +7 -7
  138. package/src/dashboard/app/pages/PhasePipeline.jsx +1 -1
  139. package/src/dashboard/app/pages/Portfolio.jsx +40 -5
  140. package/src/dashboard/scanner/activity-scanner.js +29 -0
  141. package/src/dashboard/scanner/agents-scanner.js +11 -14
  142. package/src/dashboard/scanner/features-scanner.js +45 -24
  143. package/src/dashboard/scanner/project-api.js +11 -2
  144. package/src/dashboard/scanner/skills-scanner.js +11 -1
  145. package/src/lib/detectors/index.js +1 -1
  146. package/src/lib/e2e/applicability.js +3 -1
  147. package/src/lib/git/tree-state.js +1 -16
  148. package/src/lib/graph/diff-tree.js +127 -0
  149. package/src/lib/graph/graphify-runtime.js +12 -5
  150. package/src/lib/installers/memory-installer.js +3 -3
  151. package/src/lib/installers/rules-installer.js +57 -0
  152. package/src/lib/installers/stack-assets.js +17 -0
  153. package/src/lib/installers/stack-sync.js +219 -0
  154. package/src/lib/plan/checks.js +61 -109
  155. package/src/lib/plan/critique.js +28 -0
  156. package/src/lib/plan/media.js +42 -0
  157. package/src/lib/plan/plan-html.js +125 -0
  158. package/src/lib/plan/plan-parser.js +34 -45
  159. package/src/lib/recap/piper.js +36 -0
  160. package/src/lib/recap/recap.js +232 -0
  161. package/src/lib/review-reports.js +28 -4
  162. package/src/lib/risk-detector.js +90 -11
  163. package/src/lib/specs/screen-checklist.js +33 -48
  164. package/src/lib/stack/dotnet-build-ignores.js +8 -9
  165. package/src/lib/telemetry/metrics.js +27 -29
  166. package/src/lib/validators/reuse/reuse-validator.js +1 -9
  167. package/src/lib/validators/verify-runner.js +0 -7
  168. package/src/lib/worktree/fleet-scanner.js +7 -20
  169. package/src/lib/worktree/worktree-audit.js +0 -37
  170. package/src/scripts/setup-infra.js +15 -50
  171. package/src/utils/agents-installer.js +27 -201
  172. package/src/utils/hooks-installer.js +11 -95
  173. package/framework/hooks/claude-code/core/warn-bash-source-write.js +0 -23
  174. package/framework/hooks/claude-code/statusline.py +0 -699
  175. package/framework/hooks/claude-code/statusline.sh +0 -11
  176. package/framework/hooks/shared/agent-reap.js +0 -74
  177. package/framework/hooks/shared/slice-progress.js +0 -110
  178. package/framework/skills/core/morph-hotfix/SKILL.md +0 -133
  179. package/framework/skills/core/morph-machine/SKILL.md +0 -46
  180. package/framework/skills/core/morph-review/SKILL.md +0 -129
  181. package/framework/skills/core/morph-standards/SKILL.md +0 -52
  182. package/framework/standards/PROVENANCE.json +0 -119
  183. package/framework/standards/infrastructure/neon/neon-pgrag.md +0 -243
  184. package/src/commands/project/worktree-reap.js +0 -34
  185. package/src/commands/state/handoff.js +0 -65
  186. package/src/commands/validation/mutate.js +0 -263
  187. package/src/commands/validation/scout-check.js +0 -28
  188. package/src/lib/generators/handoff-generator.js +0 -172
  189. package/src/lib/mutation/adapters.js +0 -79
  190. package/src/lib/mutation/commands.js +0 -61
  191. package/src/lib/mutation/dotnet-introspection.js +0 -281
  192. package/src/lib/mutation/manifest.js +0 -144
  193. package/src/lib/mutation/plan.js +0 -44
  194. package/src/lib/mutation/report.js +0 -68
  195. package/src/lib/mutation/runner.js +0 -301
  196. package/src/lib/mutation/schemata.js +0 -60
  197. package/src/lib/mutation/snapshot.js +0 -65
  198. package/src/lib/mutation/subset.js +0 -109
  199. package/src/lib/mutation/trx.js +0 -50
  200. package/src/lib/scout/scout-check.js +0 -293
  201. package/src/lib/validators/traceability-check.js +0 -103
  202. package/src/lib/worktree/agent-reaper.js +0 -247
@@ -0,0 +1,332 @@
1
+ #!/usr/bin/env node
2
+ // The payload is the source of truth: what Claude Code already computes (context, rate limits, worktree, PR) is
3
+ // read, never recomputed. The feature line comes from the project's own installed rules, imported at runtime,
4
+ // so this file carries no copy of them. Stateless: it never writes to disk.
5
+
6
+ import { execFileSync } from 'node:child_process';
7
+ import { existsSync, readFileSync, readdirSync, realpathSync } from 'node:fs';
8
+ import { homedir } from 'node:os';
9
+ import { join } from 'node:path';
10
+ import { pathToFileURL } from 'node:url';
11
+
12
+ const R = '\x1b[0m';
13
+ const BOLD = '\x1b[1m';
14
+ const CYAN = '\x1b[36m';
15
+ const GREEN = '\x1b[32m';
16
+ const YELLOW = '\x1b[33m';
17
+ const RED = '\x1b[31m';
18
+ const BLUE = '\x1b[34m';
19
+ const GRAY = '\x1b[90m';
20
+ const WHITE = '\x1b[97m';
21
+ const SEP = ` ${GRAY}│${R} `;
22
+
23
+ const PHASE_INDEX = { plan: 0, uiux: 1, implement: 2, review: 3 };
24
+ const PHASE_LABELS = { plan: 'planning', uiux: 'UI/UX', implement: 'implement', review: 'review' };
25
+ const GATE_PHASES = new Set(['plan', 'uiux', 'review']);
26
+
27
+ // Mirrors claude.exe: auto-compact fires at min(modelWindow, autoCompactWindow) − 20000 output reserve − 13000.
28
+ // Reproducible only when `autoCompactWindow` is set in settings; any other source is server-driven.
29
+ const COMPACT_OUTPUT_RESERVE = 20000;
30
+ const COMPACT_HEADROOM = 13000;
31
+
32
+ const int = (v, d = 0) => (Number.isFinite(Number(v)) && v !== null && v !== '' ? Math.trunc(Number(v)) : d);
33
+ const num = (v, d = 0) => (Number.isFinite(Number(v)) && v !== null && v !== '' ? Number(v) : d);
34
+
35
+ export function usageColor(pct) {
36
+ if (pct < 70) return GREEN;
37
+ return pct < 90 ? YELLOW : RED;
38
+ }
39
+
40
+ const EIGHTHS = '▏▎▍▌▋▊▉';
41
+
42
+ // Eighth blocks keep a small nonzero value visible on a 5-wide gauge.
43
+ export function progressBar(pct, width = 8) {
44
+ const exact = (Math.max(0, Math.min(100, pct)) / 100) * width;
45
+ const filled = Math.floor(exact);
46
+ let bar = '█'.repeat(filled);
47
+ if (filled < width) {
48
+ const remainder = Math.floor((exact - filled) * 8);
49
+ if (remainder) bar += EIGHTHS[remainder - 1];
50
+ }
51
+ return bar + '░'.repeat(width - [...bar].length);
52
+ }
53
+
54
+ export function formatTokens(n) {
55
+ if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1).replace(/\.0$/, '')}M`;
56
+ if (n >= 1000) return `${Math.floor(n / 1000)}k`;
57
+ return String(n);
58
+ }
59
+
60
+ export function formatCountdown(epoch, now = Date.now() / 1000) {
61
+ const remaining = epoch - now;
62
+ if (remaining <= 0) return null;
63
+ const days = Math.floor(remaining / 86400);
64
+ const rest = Math.floor(remaining % 86400);
65
+ const hours = Math.floor(rest / 3600);
66
+ const minutes = Math.floor((rest % 3600) / 60);
67
+ if (days) return `${days}d${String(hours).padStart(2, '0')}`;
68
+ if (hours) return `${hours}h${String(minutes).padStart(2, '0')}`;
69
+ return `${minutes}m`;
70
+ }
71
+
72
+ // OSC included: an OSC 8 hyperlink carries its URL as an invisible payload.
73
+ const ANSI_RE = /\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b\[[0-9;]*m/g;
74
+ // ⏳ renders double (Emoji_Presentation) while the gauge glyphs render single.
75
+ const WIDE_SINGLES = new Set([...'⚡⛔⬛⬜✅❌⏳']);
76
+
77
+ export function visibleWidth(text) {
78
+ let width = 0;
79
+ for (const ch of text.replace(ANSI_RE, '')) {
80
+ const code = ch.codePointAt(0);
81
+ width += (code >= 0x1f300 && code <= 0x1faff) || WIDE_SINGLES.has(ch) ? 2 : 1;
82
+ }
83
+ return width;
84
+ }
85
+
86
+ /** Joins `[priority, text]` parts with SEP, dropping the highest priority number until it fits `columns`. */
87
+ export function joinToWidth(parts, columns) {
88
+ const kept = parts.filter(([, text]) => text);
89
+ if (!kept.length) return '';
90
+ const sep = visibleWidth(SEP);
91
+ const total = () => kept.reduce((sum, [, text]) => sum + visibleWidth(text), 0) + sep * Math.max(0, kept.length - 1);
92
+ if (columns) {
93
+ while (kept.length > 1 && total() > columns) {
94
+ let worst = 0;
95
+ kept.forEach(([p], i) => { if (p > kept[worst][0]) worst = i; });
96
+ kept.splice(worst, 1);
97
+ }
98
+ }
99
+ return kept.map(([, text]) => text).join(SEP);
100
+ }
101
+
102
+ function readUserSettings(env = process.env) {
103
+ try {
104
+ const base = env.CLAUDE_CONFIG_DIR || join(homedir(), '.claude');
105
+ const data = JSON.parse(readFileSync(join(base, 'settings.json'), 'utf8'));
106
+ return data && typeof data === 'object' ? data : {};
107
+ } catch {
108
+ return {};
109
+ }
110
+ }
111
+
112
+ function git(args, cwd) {
113
+ return execFileSync('git', args, { cwd, stdio: ['ignore', 'pipe', 'ignore'], timeout: 3000, windowsHide: true }).toString('utf8').trim();
114
+ }
115
+
116
+ // `-uno`: porcelain v2 lists untracked entries by default, which would change what the file count means.
117
+ function gitInfo(cwd) {
118
+ let out;
119
+ try { out = git(['status', '--porcelain=v2', '--branch', '-uno'], cwd); } catch { return ''; }
120
+ let branch = '';
121
+ let ahead = 0;
122
+ let behind = 0;
123
+ let files = 0;
124
+ for (const line of out.split(/\r?\n/)) {
125
+ if (line.startsWith('# branch.head ')) branch = line.slice(14).trim();
126
+ else if (line.startsWith('# branch.ab ')) {
127
+ const m = /# branch\.ab \+(\d+) -(\d+)/.exec(line);
128
+ if (m) { ahead = Number(m[1]); behind = Number(m[2]); }
129
+ } else if ('12u'.includes(line[0] ?? 'x')) files += 1;
130
+ }
131
+ if (!branch) return '';
132
+ let ins = 0;
133
+ let dels = 0;
134
+ if (files) {
135
+ try {
136
+ const stat = git(['diff', 'HEAD', '--shortstat'], cwd);
137
+ ins = Number(/(\d+) insertion/.exec(stat)?.[1] ?? 0);
138
+ dels = Number(/(\d+) deletion/.exec(stat)?.[1] ?? 0);
139
+ } catch { /* no diff stats */ }
140
+ }
141
+ const parts = [`${BLUE} ${branch}${R}`];
142
+ if (ahead || behind) parts.push(`${ahead ? `${GREEN}↑${ahead}${R}` : ''}${behind ? `${RED}↓${behind}${R}` : ''}`);
143
+ if (ins || dels) parts.push(`${GREEN}+${ins}${RED}-${dels}${R}`);
144
+ return parts.join(' ');
145
+ }
146
+
147
+ const samePath = (a, b) => {
148
+ try { return realpathSync(a).toLowerCase() === realpathSync(b).toLowerCase(); } catch { return false; }
149
+ };
150
+
151
+ /** Worktree name of this session, or null in the primary tree. */
152
+ export function worktreeLabel(payload, cwd) {
153
+ if (payload.worktree?.name) return (payload.worktree.branch || '').replace('refs/heads/', '') || payload.worktree.name;
154
+ if (payload.workspace?.git_worktree) return payload.workspace.git_worktree;
155
+ if (!existsSync(join(cwd, '.morph', 'features'))) return null;
156
+ let out;
157
+ try { out = git(['worktree', 'list', '--porcelain'], cwd); } catch { return null; }
158
+ const entries = [];
159
+ let current = null;
160
+ for (const line of out.split(/\r?\n/)) {
161
+ if (line.startsWith('worktree ')) { current = { path: line.slice(9) }; entries.push(current); }
162
+ else if (line.startsWith('branch ') && current) current.branch = line.slice(7);
163
+ }
164
+ const mine = entries.slice(1).find((e) => samePath(e.path, cwd));
165
+ return mine ? (mine.branch || '').replace('refs/heads/', '') || null : null;
166
+ }
167
+
168
+ async function importShared(projectDir, name) {
169
+ const path = join(projectDir, '.morph', 'framework', 'hooks', 'shared', name);
170
+ return existsSync(path) ? import(pathToFileURL(path).href) : null;
171
+ }
172
+
173
+ /** In-progress features with phase, scenario progress and the open gate, read through the project's own rules. */
174
+ export async function activeFeatures(projectDir) {
175
+ const base = join(projectDir, '.morph', 'features');
176
+ if (!existsSync(base)) return [];
177
+ let stateReader;
178
+ let progressRules;
179
+ try {
180
+ [stateReader, progressRules] = await Promise.all([importShared(projectDir, 'state-reader.js'), importShared(projectDir, 'report-progress.js')]);
181
+ } catch {
182
+ return [];
183
+ }
184
+ if (!stateReader?.derivePhaseForFeature) return [];
185
+ const out = [];
186
+ for (const name of readdirSync(base).sort()) {
187
+ let feature;
188
+ try { feature = JSON.parse(readFileSync(join(base, name, 'feature.json'), 'utf8').replace(/^/, '')); } catch { continue; }
189
+ if (feature?.status !== 'in_progress') continue;
190
+ let phase = 'plan';
191
+ try { phase = stateReader.derivePhaseForFeature(name, projectDir) || 'plan'; } catch { /* plan */ }
192
+ const progress = progressRules?.implementProgress?.(join(base, name)) ?? null;
193
+ const gates = feature.approvalGates || {};
194
+ out.push({
195
+ name,
196
+ phase,
197
+ done: progress?.done ?? 0,
198
+ total: progress?.total ?? 0,
199
+ pending: GATE_PHASES.has(phase) && gates[phase]?.approved !== true ? phase : null,
200
+ });
201
+ }
202
+ return out;
203
+ }
204
+
205
+ export function renderFeatureLine(feature, columns) {
206
+ const parts = [[0, `${CYAN}${BOLD}► ${feature.name}${R}`]];
207
+ const position = PHASE_INDEX[feature.phase];
208
+ const label = PHASE_LABELS[feature.phase] ?? feature.phase;
209
+ parts.push([1, position === undefined ? `${CYAN}${label}${R}` : `${CYAN}${label} ${position + 1}/4${R}`]);
210
+ if (feature.total > 0) {
211
+ parts.push([3, `${GREEN}${progressBar((feature.done / feature.total) * 100, 6)} ${feature.done}/${feature.total} cenários${R}`]);
212
+ }
213
+ if (feature.pending) parts.push([2, `${RED}⛔ gate: ${feature.pending}${R}`]);
214
+ return joinToWidth(parts, columns);
215
+ }
216
+
217
+ /** The window /context divides by — not the model window — or null when not reproducible. */
218
+ export function effectiveWindow(payload, settings) {
219
+ const modelWindow = int(payload.context_window?.context_window_size);
220
+ if (modelWindow <= 0) return null;
221
+ if (settings.autoCompactEnabled === false) return modelWindow;
222
+ const configured = int(settings.autoCompactWindow);
223
+ return configured > 0 ? Math.min(modelWindow, configured) : null;
224
+ }
225
+
226
+ export function compactThreshold(payload, settings) {
227
+ if (settings.autoCompactEnabled === false || int(settings.autoCompactWindow) <= 0) return null;
228
+ const window = effectiveWindow(payload, settings);
229
+ if (!window) return null;
230
+ const threshold = window - COMPACT_OUTPUT_RESERVE - COMPACT_HEADROOM;
231
+ return threshold > 0 ? threshold : null;
232
+ }
233
+
234
+ export function renderStatusLine(payload, cwd, settings, columns, { gitLine = gitInfo, now } = {}) {
235
+ const parts = [];
236
+ const model = payload.model;
237
+ const modelName = model && typeof model === 'object' ? model.display_name || model.id || '' : String(model || '');
238
+ if (modelName) {
239
+ const short = modelName.replace('Claude ', '').replace('claude-', '').replace(' (1M context)', ' 1M').replace(' (claude.ai)', '');
240
+ const state = [];
241
+ if (payload.effort?.level) state.push(payload.effort.level);
242
+ if (payload.thinking?.enabled === false) state.push('no-think');
243
+ if (payload.fast_mode) state.push('fast');
244
+ parts.push([3, `${WHITE}${short}${R}${state.length ? `${GRAY}·${state.join('·')}${R}` : ''}`]);
245
+ }
246
+
247
+ const context = payload.context_window;
248
+ if (context && typeof context === 'object' && Object.keys(context).length) {
249
+ const modelWindow = int(context.context_window_size);
250
+ let tokens = int(context.total_input_tokens);
251
+ // Null right after /compact: the app's own input-only sum; output tokens are not part of it.
252
+ if (!tokens && context.current_usage && typeof context.current_usage === 'object') {
253
+ const u = context.current_usage;
254
+ tokens = int(u.input_tokens) + int(u.cache_creation_input_tokens) + int(u.cache_read_input_tokens);
255
+ }
256
+ const window = effectiveWindow(payload, settings);
257
+ let pct = null;
258
+ if (window && tokens) pct = (tokens / window) * 100;
259
+ else if (context.used_percentage !== undefined && context.used_percentage !== null) pct = num(context.used_percentage);
260
+ else if (modelWindow && tokens) pct = (tokens / modelWindow) * 100;
261
+ if (pct !== null) {
262
+ let chunk = `${usageColor(pct)}${progressBar(pct, 8)} ${pct.toFixed(0)}%${R}`;
263
+ const threshold = compactThreshold(payload, settings);
264
+ if (threshold && tokens) {
265
+ const left = threshold - tokens;
266
+ if (left <= 0) chunk += ` ${RED}⚠ compactando${R}`;
267
+ else {
268
+ const leftPct = (left / threshold) * 100;
269
+ chunk += ` ${leftPct > 25 ? GRAY : leftPct > 10 ? YELLOW : RED}↻ ${formatTokens(left)}${R}`;
270
+ }
271
+ }
272
+ parts.push([0, chunk]);
273
+ }
274
+ }
275
+
276
+ const limits = payload.rate_limits || {};
277
+ for (const [key, label, priority, countdown] of [['five_hour', '⏳', 2, true], ['seven_day', '📅', 5, false]]) {
278
+ const raw = limits[key]?.used_percentage;
279
+ if (raw === undefined || raw === null) continue;
280
+ const pct = num(raw);
281
+ let chunk = `${GRAY}${label}${R} ${usageColor(pct)}${pct.toFixed(0)}%${R}`;
282
+ const until = countdown && limits[key]?.resets_at ? formatCountdown(num(limits[key].resets_at), now) : null;
283
+ if (until) chunk += ` ${GRAY}↻ ${until}${R}`;
284
+ parts.push([priority, chunk]);
285
+ }
286
+
287
+ const branch = gitLine(cwd);
288
+ if (branch) parts.push([1, branch]);
289
+
290
+ const pr = payload.pr || {};
291
+ if (pr.number) {
292
+ const mark = { approved: '✓', changes_requested: '✗', draft: '·', pending: '…' }[pr.review_state] ?? '';
293
+ const color = { approved: GREEN, changes_requested: RED, draft: GRAY }[pr.review_state] ?? YELLOW;
294
+ let label = `PR #${pr.number} ${mark}`.trim();
295
+ if (pr.url) label = `\x1b]8;;${pr.url}\x1b\\${label}\x1b]8;;\x1b\\`;
296
+ parts.push([4, `${color}${label}${R}`]);
297
+ }
298
+ return joinToWidth(parts, columns);
299
+ }
300
+
301
+ async function readStdin() {
302
+ let raw = '';
303
+ for await (const chunk of process.stdin) raw += chunk;
304
+ return raw;
305
+ }
306
+
307
+ export async function main(raw, env = process.env, write = (s) => process.stdout.write(`${s}\n`)) {
308
+ let payload;
309
+ try {
310
+ payload = JSON.parse(raw);
311
+ } catch {
312
+ return;
313
+ }
314
+ if (!payload || typeof payload !== 'object') return;
315
+ try {
316
+ const cwd = payload.workspace?.current_dir || payload.cwd || process.cwd();
317
+ const projectDir = payload.workspace?.project_dir || cwd;
318
+ const columns = int(env.COLUMNS);
319
+ // Only in a secondary worktree: in the primary tree the line resurrects stale feature names.
320
+ if (worktreeLabel(payload, cwd)) {
321
+ for (const feature of await activeFeatures(projectDir)) write(renderFeatureLine(feature, columns));
322
+ }
323
+ const line = renderStatusLine(payload, cwd, readUserSettings(env), columns);
324
+ if (line) write(line);
325
+ } catch (err) {
326
+ write(`${RED}ERRO: ${err?.message ?? err}${R}`);
327
+ }
328
+ }
329
+
330
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
331
+ main(await readStdin());
332
+ }
@@ -252,11 +252,11 @@
252
252
  },
253
253
  "neon": {
254
254
  "phases": ["plan", "implement"],
255
- "tools": ["list_projects", "describe_table_schema", "get_database_tables", "run_sql", "explain_sql_statement", "prepare_database_migration", "complete_database_migration", "compare_database_schema", "create_branch", "list_branches", "get_connection_string"],
255
+ "tools": ["list_projects", "describe_table_schema", "get_database_tables", "run_sql", "explain_sql_statement", "prepare_database_migration", "complete_database_migration", "compare_database_schema", "create_branch", "list_branches", "get_connection_string", "list_slow_queries", "prepare_query_tuning", "complete_query_tuning", "create_snapshot", "search_docs", "get_doc"],
256
256
  "serverNames": ["neon", "Neon"],
257
257
  "detection": "mcpServers includes neon (case-insensitive)",
258
- "fallback": "SQL via the connection string (psql / dotnet ef) and the neonctl CLI for branches",
259
- "usage": "Schema inspection, migration dry-run on a throwaway branch (prepare_database_migration → complete_database_migration), query tuning. The server exposes DESTRUCTIVE tools (delete_branch, delete_project, reset_from_parent): never call one without explicit human confirmation",
258
+ "fallback": "SQL via the connection string (psql / dotnet ef) and the neon CLI for branches",
259
+ "usage": "Schema inspection, migration dry-run on a throwaway branch (prepare_database_migration → complete_database_migration), query tuning (list_slow_queries, prepare_query_tuning), snapshot before a risky migration, current Neon docs (search_docs, get_doc). Work on a branch: the Neon docs say never to connect MCP agents to production databases. The server exposes DESTRUCTIVE tools (delete_branch, delete_project, reset_from_parent): never call one without explicit human confirmation",
260
260
  "healthCheck": {
261
261
  "toolPattern": "neon",
262
262
  "testCall": "Call list_projects with limit 1",
@@ -274,7 +274,7 @@
274
274
  "credentials": [],
275
275
  "prerequisites": [],
276
276
  "stackRelevance": ["neon"],
277
- "warnings": ["OAuth against your Neon account on first use. The server can delete branches and projects: keep destructive calls behind a human."],
277
+ "warnings": ["OAuth against your Neon account on first use. The server can delete branches and projects: keep destructive calls behind a human, and point agents at a branch, never at production."],
278
278
  "setupGuide": "Run: claude mcp add --transport http neon https://mcp.neon.tech/mcp"
279
279
  }
280
280
  },
@@ -11,10 +11,16 @@
11
11
  "Write plan.md — scenarios with proof, decisions, reuse and slices",
12
12
  "Run plan-check to produce checks.json and reach the plan gate"
13
13
  ],
14
- "requiredOutputs": ["plan", "checks"],
14
+ "requiredOutputs": [
15
+ "plan",
16
+ "checks"
17
+ ],
15
18
  "optionalOutputs": [],
16
19
  "pausePoints": [
17
- { "id": "plan", "label": "Gate do plano — Approve plan.md + checks.json" }
20
+ {
21
+ "id": "plan",
22
+ "label": "Gate do plano — Approve plan.md + checks.json"
23
+ }
18
24
  ],
19
25
  "skill": "morph-plan"
20
26
  },
@@ -28,10 +34,15 @@
28
34
  "Define design system tokens and components",
29
35
  "Produce mockups and user flows"
30
36
  ],
31
- "requiredOutputs": ["uiDesignSystem"],
32
- "optionalOutputs": ["uiMockups", "uiComponents", "uiFlows"],
37
+ "requiredOutputs": [],
38
+ "optionalOutputs": [
39
+ "uiMockups"
40
+ ],
33
41
  "pausePoints": [
34
- { "id": "uiux", "label": "Approve UI/UX" }
42
+ {
43
+ "id": "uiux",
44
+ "label": "Approve UI/UX"
45
+ }
35
46
  ],
36
47
  "skill": "morph-uiux"
37
48
  },
@@ -46,7 +57,10 @@
46
57
  "One report per slice (3-implement/reports/F{n}.md, with **Arquivos:** and **Standards:**) — the implementation record"
47
58
  ],
48
59
  "requiredOutputs": [],
49
- "optionalOutputs": ["evidencias"],
60
+ "optionalOutputs": [
61
+ "implementReport",
62
+ "evidencias"
63
+ ],
50
64
  "pausePoints": [],
51
65
  "skill": "morph-apply"
52
66
  },
@@ -59,12 +73,20 @@
59
73
  "Verifier proves every scenario; reviewer (and security, when required) judge the diff at a fixed SHA",
60
74
  "verifier.md + reviewer.md (+ security.md) with SHIP/FIX and evidence-typed blockers, then Gate 3"
61
75
  ],
62
- "requiredOutputs": ["verifierReport", "reviewerReport"],
63
- "optionalOutputs": ["securityReport"],
76
+ "requiredOutputs": [
77
+ "verifierReport",
78
+ "reviewerReport"
79
+ ],
80
+ "optionalOutputs": [
81
+ "securityReport"
82
+ ],
64
83
  "pausePoints": [
65
- { "id": "review", "label": "Gate 3 — Approve the review reports" }
84
+ {
85
+ "id": "review",
86
+ "label": "Gate 3 — Approve the review reports"
87
+ }
66
88
  ],
67
- "skill": "morph-review"
89
+ "skill": "morph-apply"
68
90
  }
69
91
  }
70
92
  }
@@ -9,6 +9,14 @@ stacks:
9
9
 
10
10
  # Microsoft Agent Framework Standards
11
11
 
12
+ **Ponto de vista:** especialista no Microsoft Agent Framework (MAF 1.0 GA).
13
+ - Sweet-spot primeiro: single-agent + structured output + tools + RAG custom resolvem 95%. Semantic Kernel e Workflows só com fan-in/out genuíno.
14
+ - Alias do Model Registry, nunca modelo cravado; providers diretos (OpenAI/Google/Anthropic/Ollama), nunca hosted agent service. Para OpenAI o padrão é a Responses API; alias sem `api` significa `responses`, Chat Completions é opt-in declarado.
15
+ - Telemetria por `UseOpenTelemetry(sourceName, configure)` no builder, nos DOIS níveis (agente e chat client), `EnableSensitiveData` desligado fora de dev. `AgentOpenTelemetryConsts` e `agent.WithOpenTelemetry()` NÃO existem no pin; nomes de ActivitySource, span e métrica vêm de `ai-agents-observability-patterns`, nunca deduzidos.
16
+ - Histórico de conversa: tabela/jsonb do projeto exposta por um `ChatHistoryProvider` seu (`store: false`), nunca o `AgentSession` — ele só serve quando o histórico é SÓ a lista de mensagens, sem campo de negócio nem leitura fora do agente.
17
+ - Instruções e tools por fase: compositor determinístico em C# (um record Instructions + Tools escolhido por switch antes de montar `ChatOptions`), nem context provider nem tool — `AIContext.Tools` só ACRESCENTA tools.
18
+ - A lista abaixo é o ponto de partida: antes de concluir que um assunto não tem standard, `morph-spec standards --find "<tema>" --category ai-agents`.
19
+
12
20
  > **Layer 0/1 — resumo injetado pela rule.** São os standards core: aplicam-se a praticamente todo
13
21
  > agente MAF. O resumo de cada um entra no contexto quando você toca código de agentes.
14
22
 
@@ -18,7 +26,7 @@ stacks:
18
26
  - **ai-agents-structured-output** (`ai-agents/structured-output.md`) — Saída que alimenta outro sistema é record tipado (`RunAsync<T>` ou `ChatResponseFormat.ForJsonSchema<T>()`): `sealed record` com `[Description]` e `[JsonPropertyName]`. Raiz nunca primitivo/array (use wrapper); em streaming, `ToAgentResponseAsync()` antes de desserializar. Regra de negócio se valida depois da chamada.
19
27
  - **ai-agents-multi-agent-patterns** (`ai-agents/multi-agent-patterns.md`) — Do mais barato: agent-as-tool (`AsAIFunction()`, aninhamento ≤ 2) → sequencial → workflow; handoff vem depois de agent-as-tool, com Id estável e `WithAutonomousMode(turnLimit:)`; group chat ≤ 3 agentes. PROIBIDO: agente auxiliar DENTRO de uma tool (dois loops de function calling no mesmo turno).
20
28
  - **ai-agents-prompt-sources** (`ai-agents/prompt-sources.md`) — Prompt fixo: tabela versionada `agent_prompts` (INSERT + flip de `is_active` numa transação, nunca UPDATE; id `Guid.CreateVersion7()`). Prompt por tenant: YAML no repo → compositor determinístico → spec renderizada em jsonb. O registro de execução grava a versão EFETIVA (e o hash do blob no caminho composto).
21
- - **ai-agents-rag-custom-pgvector** (`ai-agents/rag-custom-pgvector.md`) — RAG em pgvector via EF Core: `IEmbeddingGenerator` injetado direto (sem EmbeddingService), alias `embeddings` do registry, `CosineDistance` LINQ, HNSW `vector_cosine_ops`, `tenant_id` filtrado em toda query e nunca exposto ao LLM. Ingestão delta por lookup-key: Unchanged nunca re-embeda, Removed só depois das escritas.
29
+ - **ai-agents-rag-custom-pgvector** (`ai-agents/rag-custom-pgvector.md`) — RAG em pgvector via EF Core: `IEmbeddingGenerator` injetado direto (sem EmbeddingService), alias `embeddings` do registry, `CosineDistance` LINQ, HNSW `vector_cosine_ops`, `tenant_id` filtrado em toda query e nunca exposto ao LLM. Ingestão delta por lookup-key: Unchanged nunca re-embeda, Removed só depois das escritas. Corpus com código, SKU ou nome próprio: busca híbrida com RRF (`infrastructure-neon-pgvector#hybrid-search-vector--full-text`).
22
30
  - **ai-agents-middleware-patterns** (`ai-agents/middleware-patterns.md`) — Registro sempre `.AsBuilder().Use(...).Build()`; o PRIMEIRO registrado é o mais externo. Ordem medida: timeout → usage-cost → FunctionInvokingChatClient → chat telemetry → provider. Só o delegate não-streaming rebaixa streaming em silêncio: use `Use(sharedFunc)`.
23
31
  - **ai-agents-observability-patterns** (`ai-agents/observability-patterns.md`) — Registre os DOIS ActivitySources (`Experimental.Microsoft.Agents.AI` e `Experimental.Microsoft.Extensions.AI`); a API é `builder.UseOpenTelemetry(...)` (`agent.WithOpenTelemetry()` não existe). Instrumentar os dois níveis é normal; o mesmo nível duas vezes duplica. Prompt cru só em dev com opt-in, hash em produção; turno cancelado não grava nada.
24
32
  - **ai-agents-workflows** (`ai-agents/workflows.md`) — Raro: só para fan-in/out real ou HITL longo; `foreach` + LINQ costuma bastar. `AgentWorkflowBuilder`/`WorkflowBuilder`/`InProcessExecution` vivem no pacote SEPARADO `Microsoft.Agents.AI.Workflows` 1.20.0. Agente declarativo (`Microsoft.Agents.AI.Declarative`) ainda pede `--prerelease`.
@@ -30,7 +38,7 @@ stacks:
30
38
 
31
39
  > **Layer 2 — sob demanda (sem resumo aqui).** São standards de modalidade, otimização e
32
40
  > MCP: relevantes apenas quando a feature usa especificamente esse recurso. Busque via
33
- > `morph-spec standards --find` (skill `morph-standards`) quando uma keyword corresponder — manter fora do resumo preserva o
41
+ > `morph-spec standards --find` quando uma keyword corresponder — manter fora do resumo preserva o
34
42
  > context window para o trabalho que importa.
35
43
 
36
44
  | Standard | Carregue quando a feature envolve |
@@ -8,6 +8,8 @@ stacks:
8
8
 
9
9
  # C# and .NET VSA Standards
10
10
 
11
+ **Ponto de vista:** engenheiro .NET sênior em VSA. Handler direto com `ApplicationDbContext` injetado no slice (`DbSet<T>` já é repository, o `DbContext` já é unit of work); concorrência ou SQL específico → store dedicado escrito à mão, com ADR no `plan.md`. Nunca camada Service/Manager, nunca `IRepository<T>`/`IUnitOfWork`, nunca exceção como fluxo. Abra o standard antes de escrever cada slice.
12
+
11
13
  - **architecture-vertical-slice-vertical-slice** (`architecture/vertical-slice/vertical-slice.md`) — Uma slice por operação em `Features/{Entity}Feature/{Verb}{Entity}/`: Request/Response `record`, Handler, Validator e Endpoint `IApiEndpoint`. Decorators Scrutor: Logging → Validation → Handler. PROIBIDO: MediatR/`IRequest`, AggregateRoot, DomainEvent, CQRS formal, camada Application Service, DTO compartilhado entre features, `IRepository<T>`/`IUnitOfWork`.
12
14
  - **backend-dotnet-vsa-handler-patterns** (`backend/dotnet/vsa-handler-patterns.md`) — Handler implementando `IHandler<TRequest, TResponse>` (não MediatR): injeta `ApplicationDbContext` direto e devolve `Result<T>`. Erros em `{Feature}Errors.cs`. SQL cru ou store dedicado só com justificativa em `plan.md` (`## Decisões`).
13
15
  - **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.
@@ -19,4 +21,6 @@ stacks:
19
21
  - **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.
20
22
  - **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`).
21
23
 
24
+ - **backend-dotnet-testing** (`backend/dotnet/testing.md`) — Injete `TimeProvider` (nunca `DateTime.UtcNow` direto em handler ou validator) e use `FakeTimeProvider` no teste de integração. Teste por risco: um por cenário com `risco:`, a pós-condição no nome do teste.
25
+
22
26
  > 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>`.
@@ -13,6 +13,6 @@ stacks:
13
13
 
14
14
  - **data-nosql-cache-redis** (`data/nosql/cache/redis.md`) — Cache distribuído, nunca store primário. `Microsoft.Extensions.Caching.StackExchangeRedis` 10.0.12; `IConnectionMultiplexer` singleton LAZY (nunca conectar no registro). Cache-aside no handler com `IDistributedCache` (sem wrapper); invalide a chave exata depois do `SaveChangesAsync`. PROIBIDO: cachear segredo/PII sem criptografia, omitir TTL, engolir `RedisConnectionException`.
15
15
  - **data-vector-search-rag-chunking** (`data/vector-search/rag-chunking.md`) — Prosa: sentence-aware, 256 tokens; doc técnico: semântico por headers; Q&A inteiro; código: a função inteira. Overlap 10–20%, mínimo 50 e máximo ~512 tokens. Sempre grave metadados do chunk (DocumentId, ChunkIndex, Section, SourceUrl) e reindexe quando o hash do conteúdo mudar.
16
- - **backend-database-vector-search-rag** (`backend/database/vector-search-rag.md`) — Slices VSA sobre EF Core 10 + pgvector: o handler injeta `ApplicationDbContext` e `IEmbeddingGenerator` direto (sem EmbeddingService/SearchService). Busca por `d.Embedding!.CosineDistance(query)` com a distância em ordem ASC. Sem documento relevante, `NotFound` antes de chamar o LLM. Indexação em lote: um `GenerateAsync` e um `SaveChanges`.
16
+ - **backend-database-vector-search-rag** (`backend/database/vector-search-rag.md`) — Slices VSA sobre EF Core 10 + pgvector: o handler injeta `ApplicationDbContext` e `IEmbeddingGenerator` direto (sem EmbeddingService/SearchService). Busca por `d.Embedding!.CosineDistance(query)` com a distância em ordem ASC. Sem documento relevante, `NotFound` antes de chamar o LLM. Indexação em lote: um `GenerateAsync` e um `SaveChanges`. Corpus com código, SKU ou nome próprio: busca híbrida com RRF (`infrastructure-neon-pgvector#hybrid-search-vector--full-text`).
17
17
 
18
18
  > 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>`.
@@ -0,0 +1,20 @@
1
+ ---
2
+ paths:
3
+ - "**/Migrations/**"
4
+ - "**/*DbContext*.cs"
5
+ - "**/*Configuration.cs"
6
+ - "**/Entities/**"
7
+ stacks:
8
+ - dotnet
9
+ ---
10
+
11
+ # EF Core + Neon Standards
12
+
13
+ **Ponto de vista:** especialista em EF Core sobre Neon (Postgres). Migrations explícitas, configuração fluente, navigation properties bem nomeadas, índice intencional. Nunca vaze o `DbContext` para fora do handler. Entidade duplicada vira migration divergente: confira a tabela `## Reuso` do plano antes de criar uma.
14
+
15
+ - **backend-database-postgresql-database** (`backend/database/postgresql/database.md`) — Tabelas snake_case no plural, colunas snake_case, PK `id uuid primary key` sem default na coluna: a app gera com `Guid.CreateVersion7()` (linha inserida fora da app passa o id explícito).
16
+ - **infrastructure-neon-setup** (`infrastructure/neon/neon-setup.md`) — Duas connection strings por `IConfiguration`: `DefaultConnection` no endpoint `-pooler` para o tráfego da app e `MigrationConnection` direta para as migrations.
17
+ - **infrastructure-neon-pgvector** (`infrastructure/neon/neon-pgvector.md`) — Npgsql.EntityFrameworkCore.PostgreSQL + Pgvector.EntityFrameworkCore, colunas mapeadas por `HasColumnName`, `HasPostgresExtension("vector")` no `OnModelCreating`.
18
+ - **backend-dotnet-testing** (`backend/dotnet/testing.md`) — `TimeProvider` injetado e `FakeTimeProvider` no teste; teste de integração contra o banco real do stack, um por cenário com `risco:`.
19
+
20
+ > 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
  # Frontend Standards
11
11
 
12
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.
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.
13
+ - **frontend-design-system-art-direction** (`frontend/design-system/art-direction.md`) — Tela nova ou alterada segue a `**Direção escolhida:**` do `1-design/mockups.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
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>`.
@@ -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 `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.
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. Tarefa genérica e cron usam Hangfire; job com estado que a app lê usa tabela + claim `SKIP LOCKED` (`backend-dotnet-vsa-handler-patterns#queue-on-postgres`). 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>`.
@@ -0,0 +1,25 @@
1
+ ---
2
+ paths:
3
+ - ".morph/features/*/3-implement/report.md"
4
+ ---
5
+
6
+ # Formato do report.md
7
+
8
+ A memória da implementação depois de um `/compact` e o que o `next` lê para decidir o passo.
9
+
10
+ ```markdown
11
+ | Cn | status | evidência |
12
+ |----|--------|-----------|
13
+ | C1 | feito | `OrdersTests.RecusaSemItens` |
14
+ | C2 | feito | `src/Orders/List.cs:31` |
15
+ | C3 | bloqueado | |
16
+
17
+ ## Notas
18
+ - 2026-10-27 — C3: o plano não diz se pedido cancelado volta ao estoque. Pergunta: devolve?
19
+ ```
20
+
21
+ - Status: `pendente` | `feito` | `bloqueado`. Uma linha por `Cn` do `checks.json`, sem inventar nem pular.
22
+ - `feito` exige evidência: o teste que prova, o `arquivo:linha` (`prova: leitura`) ou `evidencias/{Cn}.md` (`eval`/`manual`). `feito` sem evidência é o que o hook de Stop aponta.
23
+ - `bloqueado` só com a pergunta em `## Notas`: o `next` devolve `decide` e ela vira `AskUserQuestion`. Respondida, a resposta vai em `## Notas` e o `Cn` volta a `pendente`.
24
+ - `## Notas` é append-only, uma linha datada por decisão ou fato novo. Fato errado do plano vai na `## Errata` do `plan.md`, não aqui.
25
+ - Atualize ao fechar cada `Cn` e commite junto (`wip(<f>): Cn …`).
@@ -10,6 +10,8 @@ stacks:
10
10
 
11
11
  # Infrastructure Standards
12
12
 
13
+ **Ponto de vista:** jobs assíncronos (Hangfire só com razão real), deploy e observabilidade — retry, idempotência, dead-letter, métricas. Não invente infra: use o que o stack já oferece. GitHub (issues, PRs, releases, Actions) sempre pelo `gh` no Bash, nunca MCP. Coolify sempre por SSH + `curl` na API local — não há MCP nem CLI; o token é lido do lado do servidor e nunca impresso. Recurso pago acima de US$ 10/mês → ADR em `## Decisões` antes de criar.
14
+
13
15
  - **infrastructure-docker-coolify-deploy** (`infrastructure/docker/coolify-deploy.md`) — Acesso só por SSH na VPS, API do Coolify em `localhost:8000/api/v1` com token server-side (nunca impresso). Env vars no Coolify, nunca no Dockerfile. Endpoint `/health`. Redeploy por `POST /api/v1/deploy?uuid=` (GET não deploya); sem `deployment_uuid` na resposta, nada foi deployado.
14
16
  - **infrastructure-docker-local-compose-isolation** (`infrastructure/docker/local-compose-isolation.md`) — Cada worktree roda o próprio stack compose; portas do bloco da worktree (`PORT`, `API_PORT`, `POSTGRES_PORT`, `REDIS_PORT`) publicadas como `${VAR:-default}:container`. PROIBIDO: porta fixa no host, `container_name`. Healthcheck em todo serviço. Compose que lê a porta por outro nome: declare em `e2e.portEnvNames`.
15
17
 
@@ -10,6 +10,6 @@ stacks:
10
10
 
11
11
  # Neon Auth Standards
12
12
 
13
- - **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`.
13
+ - **backend-integrations-neon-auth-neon-auth** (`backend/integrations/neon-auth/neon-auth.md`) — SDK `@neon/auth` (≥ 0.8.1; `@neondatabase/auth` é o nome antigo): `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`.
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,9 +11,9 @@ 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 `plan.md` (`## Decisões`), default `gen_random_uuid()`.
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
- - **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`.
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()`. Busca híbrida: `tsvector` gerado + GIN + pgvector fundidos por RRF, nunca soma de scores. No Neon, `pg_search` saiu, `pgrag` está deprecado, `pgmq` e Apache AGE estão fora do catálogo; `lakebase_text` só com ADR.
15
+ - **infrastructure-neon-platform** (`infrastructure/neon/neon-platform.md`) — O que o Neon já entrega: branch descartável, schema diff, snapshot antes de migration com `degrau:`, Object Storage, AI Gateway, diagnóstico de consulta, read replica. A região limita o catálogo (`sa-east-1` só tem Postgres e Auth). MCP nunca em produção: agente trabalha em branch.
16
+ - **backend-integrations-neon-auth-neon-auth** (`backend/integrations/neon-auth/neon-auth.md`) — SDK `@neon/auth` (≥ 0.8.1; `@neondatabase/auth` é o nome antigo): `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
 
18
18
  > 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>`.
19
19
 
@@ -8,6 +8,8 @@ stacks:
8
8
 
9
9
  # Next.js Standards
10
10
 
11
+ **Ponto de vista:** Next.js App Router idiomático — Server Component por padrão, Client Component só com interação real; Tailwind + shadcn + Motion; tipos por Zod. Tipo local redeclarado em cada arquivo é o defeito mais comum aqui: confira a tabela `## Reuso` do plano antes de criar um tipo.
12
+
11
13
  - **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
14
  - **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
15
  - **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.
@@ -22,4 +24,9 @@ stacks:
22
24
  - **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.
23
25
  - **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`).
24
26
 
27
+ - **frontend-admin-app-data-table** (`frontend/admin-app/data-table.md`) — Toda lista do admin pagina, ordena e filtra no servidor, com `page/pageSize/sort/dir/filters` na URL (Zod com `.catch`) e a API devolvendo `{ items, totalCount }` sobre um `OrderBy` com desempate único.
28
+ - **frontend-admin-app-forms** (`frontend/admin-app/forms.md`) — O padrão de `nextjs/forms.md` mais os deltas de CRUD: `ValidationProblemDetails` do .NET mapeado para cada campo por `setError`.
29
+ - **frontend-admin-app-feedback** (`frontend/admin-app/feedback.md`) — Um `ConfirmDialog` (AlertDialog controlado) só para ação destrutiva irreversível, em lote ou que descarta mudança; ação reversível usa toast com desfazer.
30
+ - **frontend-admin-app-page-structure** (`frontend/admin-app/page-structure.md`) — Decida por tela e registre no checklist do mockup: abas para 3+ seções independentes, página longa com navegação fixa quando lidas juntas, wizard quando um campo depende do anterior.
31
+
25
32
  > 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>`.