@polymorphism-tech/morph-spec 8.37.0 → 8.38.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 (130) hide show
  1. package/CLAUDE.md +19 -6
  2. package/README.md +2 -2
  3. package/bin/morph-spec.js +16 -7
  4. package/claude-plugin.json +1 -1
  5. package/docs/CHEATSHEET.md +1 -1
  6. package/docs/QUICKSTART.md +1 -1
  7. package/framework/CLAUDE.md +3 -3
  8. package/framework/MORPH.md +25 -22
  9. package/framework/agents.json +76 -27
  10. package/framework/docs/CLI.md +55 -18
  11. package/framework/evals/rubrics/code-quality.md +5 -0
  12. package/framework/evals/rubrics/condensed.md +6 -2
  13. package/framework/evals/rubrics/contracts.md +20 -0
  14. package/framework/evals/rubrics/test-coverage.md +17 -6
  15. package/framework/hooks/README.md +9 -3
  16. package/framework/hooks/claude-code/core/graph-refresh-kick.js +52 -0
  17. package/framework/hooks/claude-code/core/telemetry-log.js +172 -87
  18. package/framework/hooks/claude-code/core/validate-completion.js +16 -90
  19. package/framework/hooks/claude-code/core/worktree-context.js +10 -0
  20. package/framework/hooks/shared/command-tokens.js +96 -0
  21. package/framework/hooks/shared/git-exclude.js +44 -0
  22. package/framework/hooks/shared/git-paths.js +52 -0
  23. package/framework/hooks/shared/graph-freshness.js +380 -0
  24. package/framework/hooks/shared/graph-refresh.js +416 -0
  25. package/framework/hooks/shared/morph-cli.js +17 -5
  26. package/framework/hooks/shared/redact-secrets.js +55 -0
  27. package/framework/hooks/shared/telemetry-logger.js +25 -2
  28. package/framework/hooks/shared/transcript-usage.js +80 -0
  29. package/framework/rubrics/backend.json +2 -2
  30. package/framework/rubrics/default.json +2 -2
  31. package/framework/rubrics/frontend.json +2 -2
  32. package/framework/rules/csharp-standards.md +1 -0
  33. package/framework/rules/morph-workflow.md +1 -0
  34. package/framework/rules/nextjs-standards.md +1 -0
  35. package/framework/schemas/config.schema.json +41 -1
  36. package/framework/schemas/feature.schema.json +5 -1
  37. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  38. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  39. package/framework/skills/core/morph-apply/SKILL.md +36 -11
  40. package/framework/skills/core/morph-brainstorming/SKILL.md +16 -7
  41. package/framework/skills/core/morph-eval/SKILL.md +1 -1
  42. package/framework/skills/core/morph-hotfix/SKILL.md +1 -1
  43. package/framework/skills/core/morph-implement/SKILL.md +39 -10
  44. package/framework/skills/core/morph-media/SKILL.md +1 -1
  45. package/framework/skills/core/morph-plan/SKILL.md +72 -15
  46. package/framework/skills/core/morph-proposal/SKILL.md +1 -1
  47. package/framework/skills/core/morph-review/SKILL.md +51 -13
  48. package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
  49. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  50. package/framework/skills/core/morph-uiux/SKILL.md +34 -9
  51. package/framework/standards/STANDARDS.json +187 -12
  52. package/framework/standards/ai-agents/rag-custom-pgvector.md +7 -3
  53. package/framework/standards/ai-agents/testing-ai.md +2 -1
  54. package/framework/standards/architecture/code-comments.md +64 -0
  55. package/framework/standards/backend/dotnet/testing.md +22 -0
  56. package/framework/standards/frontend/admin-app/data-table.md +562 -0
  57. package/framework/standards/frontend/admin-app/feedback.md +236 -0
  58. package/framework/standards/frontend/admin-app/forms.md +232 -0
  59. package/framework/standards/frontend/admin-app/page-structure.md +203 -0
  60. package/framework/standards/frontend/nextjs/components.md +37 -58
  61. package/framework/standards/frontend/nextjs/data-fetching.md +1 -0
  62. package/framework/standards/frontend/nextjs/forms.md +31 -30
  63. package/framework/standards/frontend/nextjs/state-management.md +2 -1
  64. package/framework/standards/frontend/nextjs/testing.md +18 -0
  65. package/framework/templates/context/design-system.md +3 -3
  66. package/framework/templates/docs/proposal.md +11 -0
  67. package/framework/templates/docs/spec.md +15 -0
  68. package/framework/templates/docs/ui-mockups.md +115 -98
  69. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaPlan.cs +12 -6
  70. package/framework/templates/feature/decisions.md +124 -122
  71. package/package.json +1 -1
  72. package/src/commands/graph/affected.js +4 -3
  73. package/src/commands/graph/build.js +109 -18
  74. package/src/commands/graph/explain.js +8 -5
  75. package/src/commands/graph/status.js +14 -8
  76. package/src/commands/observability/cost.js +148 -18
  77. package/src/commands/project/doctor.js +82 -10
  78. package/src/commands/project/update.js +25 -0
  79. package/src/commands/project/worktree.js +73 -90
  80. package/src/commands/state/advance.js +18 -0
  81. package/src/commands/state/approve.js +15 -0
  82. package/src/commands/state/archive.js +62 -9
  83. package/src/commands/state/finish.js +62 -14
  84. package/src/commands/state/gate-check.js +8 -1
  85. package/src/commands/state/gate-decision.js +8 -4
  86. package/src/commands/state/report-check.js +90 -9
  87. package/src/commands/state/usecase.js +66 -0
  88. package/src/commands/tasks/dag.js +17 -2
  89. package/src/commands/validation/validate-feature.js +6 -1
  90. package/src/commands/validation/verify.js +20 -1
  91. package/src/dashboard/scanner/agents-scanner.js +5 -1
  92. package/src/dashboard/scanner/hooks-scanner.js +8 -1
  93. package/src/dashboard/scanner/standards-scanner.js +2 -1
  94. package/src/lib/ai/comment-stripper.js +101 -4
  95. package/src/lib/evaluator-report.js +25 -0
  96. package/src/lib/generators/handoff-generator.js +4 -18
  97. package/src/lib/git/default-branch.js +52 -0
  98. package/src/lib/git/tree-fingerprint.js +58 -0
  99. package/src/lib/git/tree-state.js +167 -0
  100. package/src/lib/graph/god-nodes.js +12 -3
  101. package/src/lib/graph/graph-health.js +48 -2
  102. package/src/lib/graph/graph-loader.js +99 -25
  103. package/src/lib/graph/graphifyignore.js +145 -28
  104. package/src/lib/graph/sanitize.js +117 -11
  105. package/src/lib/risk-detector.js +90 -0
  106. package/src/lib/specs/acervo-format.js +70 -0
  107. package/src/lib/specs/acervo-lifecycle.js +264 -0
  108. package/src/lib/specs/adr-index.js +169 -0
  109. package/src/lib/specs/revocations.js +156 -0
  110. package/src/lib/specs/screen-checklist.js +357 -0
  111. package/src/lib/specs/usecase-check.js +20 -16
  112. package/src/lib/specs/usecase-promoter.js +103 -38
  113. package/src/lib/specs/usecase-registry.js +7 -7
  114. package/src/lib/tasks/tasks-json-parser.js +2 -3
  115. package/src/lib/telemetry/metrics.js +386 -0
  116. package/src/lib/telemetry/telemetry-reader.js +161 -51
  117. package/src/lib/telemetry/verify-event.js +79 -0
  118. package/src/lib/validators/code-budget/code-budget-validator.js +172 -0
  119. package/src/lib/validators/code-budget/comment-scanner.js +75 -0
  120. package/src/lib/validators/code-budget/index.js +9 -0
  121. package/src/lib/validators/reuse/reuse-validator.js +4 -2
  122. package/src/lib/validators/reuse/symbol-scanner.js +27 -22
  123. package/src/lib/validators/validation-runner.js +21 -5
  124. package/src/lib/validators/verify-runner.js +11 -0
  125. package/src/lib/worktree/finish-backup.js +739 -0
  126. package/src/lib/worktree/ignored-triage.js +15 -8
  127. package/src/lib/worktree/worktree-audit.js +5 -0
  128. package/src/scripts/setup-infra.js +29 -18
  129. package/src/utils/agents-installer.js +281 -12
  130. package/src/utils/hooks-installer.js +75 -15
@@ -1541,13 +1541,17 @@
1541
1541
  "fake clock",
1542
1542
  "integration test",
1543
1543
  "ProblemDetails",
1544
- "error body assertion"
1544
+ "error body assertion",
1545
+ "test budget",
1546
+ "orçamento de teste",
1547
+ "[Theory] table"
1545
1548
  ],
1546
1549
  "anchors": {
1547
1550
  "clock": "#rule-1--inject-timeprovider-fake-it-with-faketimeprovider-in-tests",
1548
- "errorbody": "#rule-2--error-path-tests-assert-the-response-body-not-just-the-status-code"
1551
+ "errorbody": "#rule-2--error-path-tests-assert-the-response-body-not-just-the-status-code",
1552
+ "test-budget": "#test-budget"
1549
1553
  },
1550
- "digest": "Inject TimeProvider (never DateTime.UtcNow / DateOnly.FromDateTime(DateTime.UtcNow) directly in a handler or validator) and fake it with FakeTimeProvider (Microsoft.Extensions.TimeProvider.Testing) in integration tests — deterministic, no midnight-UTC flake, boundary dates (today vs. yesterday) become facts about the fake clock instead of when the suite happens to run. xUnit + WebApplicationFactory error-path tests (400/404/409) must assert the ProblemDetails response BODY (title/detail/violated field or error code), never just the HTTP status code — a status-only assertion lets the wrong validation rule or the wrong error branch pass silently as long as the status matches.",
1554
+ "digest": "Inject TimeProvider (never DateTime.UtcNow / DateOnly.FromDateTime(DateTime.UtcNow) directly in a handler or validator) and fake it with FakeTimeProvider (Microsoft.Extensions.TimeProvider.Testing) in integration tests — deterministic, no midnight-UTC flake, boundary dates (today vs. yesterday) become facts about the fake clock instead of when the suite happens to run. xUnit + WebApplicationFactory error-path tests (400/404/409) must assert the ProblemDetails response BODY (title/detail/violated field or error code), never just the HTTP status code — a status-only assertion lets the wrong validation rule or the wrong error branch pass silently as long as the status matches. Test budget per task: one test per postcondition plus one regression test per bug, more only with a one-line justification in the task; scenarios of the same behavior (edge cases included) are rows of one [Theory], never copied [Fact]s; no test whose only assertion is a copy, config or prose string (that is an analyzer or a publish-time validation, outside the suite count); shared fixtures before new helpers; TDD for bugs (regression first) and pure logic; a surviving mutant is killed by tightening an assertion or adding a row, not by a new test.",
1551
1555
  "verifiedAgainst": ".NET 10 + xUnit 2 + `Microsoft.Extensions.TimeProvider.Testing`.",
1552
1556
  "provedBy": []
1553
1557
  },
@@ -2136,8 +2140,8 @@
2136
2140
  "patterns": "#three-tier-hierarchy",
2137
2141
  "shadcn": "#tier-1--shadcnui-primitives"
2138
2142
  },
2139
- "digest": "Server Components for data fetching and static content. Client Components for interactivity. shadcn/ui components are copy-paste — customize in src/components/ui/. Never use index.tsx for named exports. One component per file. Avoid prop drilling — use composition or Context API.",
2140
- "verifiedAgainst": "Next.js 15 + shadcn/ui CLI + TanStack Table 8.",
2143
+ "digest": "Three tiers: components/ui is shadcn CLI output (never edit, wrap instead); components/ composes primitives with no domain knowledge; features/*/components knows the domain. 'use client' only for hooks, events or browser APIs; never pass raw API data as props — type and validate it first. The shared data table is specified once, in admin-app/data-table.md.",
2144
+ "verifiedAgainst": "Next.js 16 + shadcn 4 CLI (the code blocks typecheck under `tsc --strict`).",
2141
2145
  "provedBy": []
2142
2146
  },
2143
2147
  {
@@ -2185,15 +2189,15 @@
2185
2189
  "react-hook-form",
2186
2190
  "Zod",
2187
2191
  "form validation",
2188
- "Server Actions forms",
2189
- "useFormState"
2192
+ "shadcn Field",
2193
+ "Controller"
2190
2194
  ],
2191
2195
  "anchors": {
2192
2196
  "validation": "#schema-composition",
2193
2197
  "submission": "#complete-form-pattern"
2194
2198
  },
2195
- "digest": "react-hook-form with Zod resolver for client forms. Define Zod schema before component. Server Actions for mutations: 'use server' directive. Use useFormState/useActionState for Server Action feedback. Never submit to API routes — use Server Actions or React Query mutations. Display field-level errors from Zod.",
2196
- "verifiedAgainst": "Next.js 15 + react-hook-form 7 + Zod 4 + @hookform/resolvers 3.",
2199
+ "digest": "react-hook-form + Zod + shadcn Field components wired through Controller. Define the Zod schema first and derive the type with z.infer (z.input/z.output when it coerces); zodResolver connects it; Field/FieldLabel/FieldError render each field and its error; never useState per field; submit through TanStack Query useMutation and put a failed submit on root. Edit forms reuse the create schema (.partial()). Zod 4: z.email(), a single `error` param. A legacy shadcn Form/FormField screen is migrated when touched, never extended. Admin CRUD forms add the deltas of admin-app/forms.md.",
2200
+ "verifiedAgainst": "Next.js 16 + react-hook-form 7 + Zod 4 + @hookform/resolvers 5 + shadcn 4 `Field` (the code blocks typecheck under `tsc --strict`).",
2197
2201
  "provedBy": []
2198
2202
  },
2199
2203
  {
@@ -2357,13 +2361,17 @@
2357
2361
  "testing-library",
2358
2362
  "Playwright",
2359
2363
  "Next.js testing",
2360
- "component tests"
2364
+ "component tests",
2365
+ "test budget",
2366
+ "orçamento de teste",
2367
+ "it.each table"
2361
2368
  ],
2362
2369
  "anchors": {
2363
2370
  "unit": "#component-test-pattern",
2364
- "e2e": "#test-file-co-location"
2371
+ "e2e": "#test-file-co-location",
2372
+ "test-budget": "#test-budget"
2365
2373
  },
2366
- "digest": "Vitest for unit tests, React Testing Library for component tests. Mock next/navigation with vi.mock. Test Server Actions with direct function calls. Use MSW for API mocking in component tests. Playwright for E2E tests against running app. Never test implementation details — test user behavior and output.",
2374
+ "digest": "Vitest for unit tests, React Testing Library for component tests. Mock next/navigation with vi.mock. Test Server Actions with direct function calls. Use MSW for API mocking in component tests. Playwright for E2E tests against running app. Never test implementation details — test user behavior and output. Test budget per task: one test per postcondition plus one regression test per bug, more only with a one-line justification in the task; scenarios of the same behavior are rows of one it.each, never copied it() blocks; no test whose only assertion is a copy string (lint or publish-time validation instead); shared wrappers, MSW handlers and factories before new helpers; TDD for bugs and pure logic, component tests may come after the code.",
2367
2375
  "verifiedAgainst": "Vitest 3 + React Testing Library 16 + MSW 2.",
2368
2376
  "provedBy": []
2369
2377
  },
@@ -2684,6 +2692,165 @@
2684
2692
  "digest": "MCP tools extend Claude with external data and actions. Tools are synchronous request-response with typed inputSchema. Resources provide read-only data access. Prompts are reusable templates. Register MCP servers in .claude/settings.json. Prefer @context7 for live library docs. Test tools with MCP Inspector before integrating.",
2685
2693
  "verifiedAgainst": "Claude Code MCP tooling — context7, Playwright; GitHub via `gh` CLI; Neon via `neonctl`.",
2686
2694
  "provedBy": []
2695
+ },
2696
+ {
2697
+ "id": "frontend-admin-app-data-table",
2698
+ "name": "data table",
2699
+ "path": "frontend/admin-app/data-table.md",
2700
+ "category": "frontend",
2701
+ "subcategory": "admin-app",
2702
+ "tags": [
2703
+ "frontend",
2704
+ "admin-app"
2705
+ ],
2706
+ "stacks": [
2707
+ "nextjs"
2708
+ ],
2709
+ "aliases": [
2710
+ "data table",
2711
+ "admin list",
2712
+ "server-side pagination",
2713
+ "paginação",
2714
+ "tabela de dados",
2715
+ "ordenação no servidor",
2716
+ "empty state",
2717
+ "row selection",
2718
+ "bulk actions",
2719
+ "manualPagination"
2720
+ ],
2721
+ "anchors": {
2722
+ "contract": "#the-list-contract-url-and-api",
2723
+ "table": "#the-shared-datatable-tier-2",
2724
+ "states": "#the-three-states",
2725
+ "mobile": "#mobile"
2726
+ },
2727
+ "digest": "Every admin list is paged, sorted and filtered on the server, with page/pageSize/sort/dir/filters in the URL (searchParams parsed by a Zod schema with .catch defaults) and the API answering { items, totalCount } over an OrderBy plus a unique id tiebreaker. The Server Component page reads searchParams and wraps the results in a Suspense keyed by the query; one shared components/data-table.tsx on TanStack Table 9 sets manualPagination + rowCount, manualSorting and selection by a stable getRowId, and only writes the URL (router.replace with scroll:false, page reset on any filter/sort change). Three non-happy states always: loading skeleton, empty (no records vs no match for the filters), error as a value with retry via router.refresh(). Compact density, right-aligned tabular numbers, one inline row action plus a menu, a bulk bar over the selected page, a card list below md. Never 'first N + see all' instead of paging.",
2728
+ "verifiedAgainst": "Next.js 16 App Router + @tanstack/react-table 9 + Zod 4 + shadcn 4 (the code blocks typecheck under `tsc --strict`).",
2729
+ "provedBy": []
2730
+ },
2731
+ {
2732
+ "id": "frontend-admin-app-feedback",
2733
+ "name": "feedback",
2734
+ "path": "frontend/admin-app/feedback.md",
2735
+ "category": "frontend",
2736
+ "subcategory": "admin-app",
2737
+ "tags": [
2738
+ "frontend",
2739
+ "admin-app"
2740
+ ],
2741
+ "stacks": [
2742
+ "nextjs"
2743
+ ],
2744
+ "aliases": [
2745
+ "confirmation dialog",
2746
+ "confirm dialog",
2747
+ "delete confirmation",
2748
+ "toast",
2749
+ "sonner",
2750
+ "toast messages",
2751
+ "undo toast",
2752
+ "diálogo de confirmação"
2753
+ ],
2754
+ "anchors": {
2755
+ "when": "#when-to-confirm",
2756
+ "dialog": "#the-confirmation-dialog-tier-2",
2757
+ "catalog": "#the-toast-catalog"
2758
+ },
2759
+ "digest": "One ConfirmDialog (controlled shadcn AlertDialog, a plain Button that awaits the action, stays open showing the error on failure, optional type-to-confirm) used only for destructive-irreversible, bulk or discard-changes actions; a reversible action gets a 'Desfazer' toast instead. The title names the object and the confirm label repeats the verb. Every toast string comes from a catalog (lib/toast-messages.ts for generic outcomes + per-entity EntityMessages, because Portuguese agrees in gender and number); sonner toast.success/toast.error with an id per action, a longer duration for errors, errors mapped by status (403, 409, network), never the server's raw text, and field validation never in a toast.",
2760
+ "verifiedAgainst": "shadcn 4 `AlertDialog` (Radix) + sonner 2 + TanStack Query 5 (the code blocks typecheck under `tsc --strict`).",
2761
+ "provedBy": []
2762
+ },
2763
+ {
2764
+ "id": "frontend-admin-app-forms",
2765
+ "name": "forms",
2766
+ "path": "frontend/admin-app/forms.md",
2767
+ "category": "frontend",
2768
+ "subcategory": "admin-app",
2769
+ "tags": [
2770
+ "frontend",
2771
+ "admin-app"
2772
+ ],
2773
+ "stacks": [
2774
+ "nextjs"
2775
+ ],
2776
+ "aliases": [
2777
+ "admin form",
2778
+ "crud form",
2779
+ "server validation errors",
2780
+ "ValidationProblemDetails",
2781
+ "unsaved changes",
2782
+ "formulário"
2783
+ ],
2784
+ "anchors": {
2785
+ "server-errors": "#server-errors-on-the-field",
2786
+ "placement": "#where-the-form-lives",
2787
+ "dirty": "#unsaved-changes"
2788
+ },
2789
+ "digest": "Admin forms use the one pattern of nextjs/forms.md (Zod schema, zodResolver, react-hook-form Controller + shadcn Field/FieldError, useMutation) plus the CRUD deltas: map .NET ValidationProblemDetails errors onto their fields with setError (unmatched ones go to root.serverError), mode 'onTouched', submit disabled only while saving, form.reset(values) after a successful save, a guard for unsaved changes, the three useForm generics (z.input / unknown / z.output) when the schema coerces, and placement by size: dialog up to 4 fields, sheet or page up to 12, its own page beyond that, a wizard when steps depend on each other.",
2790
+ "verifiedAgainst": "react-hook-form 7 + @hookform/resolvers 5 + Zod 4 + TanStack Query 5 + shadcn 4 `Field` (the code blocks typecheck under `tsc --strict`).",
2791
+ "provedBy": []
2792
+ },
2793
+ {
2794
+ "id": "frontend-admin-app-page-structure",
2795
+ "name": "page structure",
2796
+ "path": "frontend/admin-app/page-structure.md",
2797
+ "category": "frontend",
2798
+ "subcategory": "admin-app",
2799
+ "tags": [
2800
+ "frontend",
2801
+ "admin-app"
2802
+ ],
2803
+ "stacks": [
2804
+ "nextjs"
2805
+ ],
2806
+ "aliases": [
2807
+ "tabs",
2808
+ "abas",
2809
+ "long page",
2810
+ "página longa",
2811
+ "wizard",
2812
+ "multi-step form",
2813
+ "stepper",
2814
+ "detail page"
2815
+ ],
2816
+ "anchors": {
2817
+ "decision": "#tabs-long-page-or-wizard",
2818
+ "tabs": "#tabs-in-the-url",
2819
+ "wizard": "#wizard-for-long-flows"
2820
+ },
2821
+ "digest": "Decide per screen and write it in the mockup checklist: tabs for 3+ independent sections, a long page with sticky section nav when sections are read together or share one save, a plain page for 2 short blocks, a wizard when later fields depend on earlier answers. The active tab lives in ?tab= and only its content is rendered and fetched on the server; switching tab drops the list params of the tab being left. Unbounded related lists get their own paged tab, never a capped preview. A wizard keeps one react-hook-form across steps, validates each step with trigger(stepFields), never loses data going back, and commits on a final review step.",
2822
+ "verifiedAgainst": "Next.js 16 App Router + shadcn 4 `Tabs` (Radix) + react-hook-form 7 + Zod 4 (the code blocks typecheck under `tsc --strict`).",
2823
+ "provedBy": []
2824
+ },
2825
+ {
2826
+ "id": "architecture-code-comments",
2827
+ "name": "code comments",
2828
+ "path": "architecture/code-comments.md",
2829
+ "category": "architecture",
2830
+ "subcategory": null,
2831
+ "tags": [
2832
+ "architecture",
2833
+ "comments"
2834
+ ],
2835
+ "stacks": [
2836
+ "*"
2837
+ ],
2838
+ "aliases": [
2839
+ "code comments",
2840
+ "comentário de código",
2841
+ "comment policy",
2842
+ "política de comentário",
2843
+ "comentário de processo",
2844
+ "self-documenting code"
2845
+ ],
2846
+ "anchors": {
2847
+ "comment-policy": "#o-que-merece-comentário",
2848
+ "not-worth": "#o-que-não-merece",
2849
+ "size": "#tamanho"
2850
+ },
2851
+ "digest": "Comment only the WHY the code cannot show: an invariant the type does not express, an order that matters, an external quirk (third-party API, library bug with its link), a public API contract. Never process history: task, UC, ADR, FR, Gate, evaluator, spec.md and mockup belong in the commit message and decisions.md. Do not restate the code (a good name replaces the comment), do not describe the world outside the file (it goes stale), and turn claims about behavior into test assertions. Ceiling ~3 lines; longer needs a reason. The verify validators node (code-budget) warns with the count of added comment lines that cite process.",
2852
+ "verifiedAgainst": "C#, TypeScript e JavaScript (convenção de linguagem, sem pacote externo).",
2853
+ "provedBy": []
2687
2854
  }
2688
2855
  ],
2689
2856
  "sharedAliases": {
@@ -2767,6 +2934,14 @@
2767
2934
  "seedance": [
2768
2935
  "ai-agents-media-video",
2769
2936
  "frontend-design-system-ai-video-generation"
2937
+ ],
2938
+ "test budget": [
2939
+ "backend-dotnet-testing",
2940
+ "frontend-nextjs-testing"
2941
+ ],
2942
+ "orçamento de teste": [
2943
+ "backend-dotnet-testing",
2944
+ "frontend-nextjs-testing"
2770
2945
  ]
2771
2946
  }
2772
2947
  }
@@ -188,7 +188,10 @@ The agent decides autonomously when to call `VectorSearchTool.SearchAsync`. It r
188
188
 
189
189
  ## Ingestão delta por lookup-key
190
190
 
191
- **O defeito que esta seção existe para matar está em produção e foi medido.** `KbStore.IngestAsync` do GHLBrain faz `DELETE … WHERE location_id AND source` e re-insere/re-embeda **tudo** numa transação (`docs/specs/elevacao-9/brutos/scan-ghlbrain.md:412`): 288 itens re-embedados a cada republish do catálogo, e uma **janela** entre o apagar e o gravar em que a busca não acha nada.
191
+ **O defeito que esta seção existe para matar é o apaga-e-regrava.** `KbStore.IngestAsync` do GHLBrain faz `DELETE … WHERE location_id AND source` e re-insere/re-embeda **tudo** do `source` a cada ingestão, mudou ou não (`docs/specs/elevacao-9/brutos/scan-ghlbrain.md:412`): 288 fichas re-embedadas, uma chamada de embedding por item, a cada reindex em massa do catálogo — e, com id posicional, inserir um trecho no topo de um documento torna "novos" todos os de baixo.
192
+
193
+ > [!warning] Errata (2026-09-15, medida no código e no Neon de produção — Morph.GHLBrain#96)
194
+ > A versão anterior deste parágrafo afirmava também uma **janela** entre o apagar e o gravar em que a busca não acha nada. **No GHLBrain ela não existe:** `ConvVectorSql.ExecuteAsync` roda o `DELETE` e os `INSERT`s numa transação só, e o embedding acontece **antes** de a transação abrir — em READ COMMITTED o leitor vê o conteúdo antigo até o commit. O custo medido também é pequeno (324 trechos, ~US$ 0,0005 por re-embed completo, 1 trecho por `source`): lá, o ganho real do delta é **latência** (o reindex em massa) e **prevenção** (documento de até ~70 trechos). A janela é real em quem apaga fora da transação que grava — é o caso que a regra 4 abaixo cobre.
192
195
 
193
196
  ### O contrato — uma função pura, sem I/O
194
197
 
@@ -220,7 +223,8 @@ public sealed record DeltaPlan<T>(
220
223
  1. **`Unchanged` não é re-embedado.** É a economia inteira. Num republish típico a lista grande é `Unchanged` e as outras três são curtas — e é exatamente isso que a ingestão anterior escondia.
221
224
  2. **`Removed` são as chaves presentes no acervo e ausentes do `incoming`.** Sem esse balde, conteúdo apagado na origem continua sendo devolvido pela busca.
222
225
  3. **O id continua determinístico** — `DeterministicUuid.V5(namespace, "{loc}:{source}:{idx}")` já é a prática — para que `Updated` seja **upsert**, não delete+insert. Delete+insert quebra referências e troca o id de um chunk que só mudou de texto.
223
- 4. **Sem janela vazia: nunca apague tudo antes de gravar.** É literalmente o que faz a busca devolver vazio durante a reingestão. Aplique `Added`/`Updated` primeiro e só então `Removed`, dentro da mesma transação.
226
+ **Ressalva medida (Morph.GHLBrain#96, ADR-2 da `delta-ingestion`):** quando o item é um *trecho* de documento, a posição como lookup-key derrota a regra 1 — inserir um trecho no topo desloca os índices e marca todo trecho abaixo como `Updated`, re-embedando tudo. Ali a chave certa é o **hash do conteúdo** (`lookupKey` e `contentHash` recebem o mesmo delegado): `Updated` fica vazio por construção, editar um trecho vira `Added` + `Removed`, e trechos idênticos são deduplicados antes do `Plan` para a chave duplicada nunca disparar. A chave posicional continua certa quando o item tem identidade própria (uma ficha por produto, um documento inteiro).
227
+ 4. **Sem janela vazia: nunca apague antes de gravar fora da transação que grava.** Apagar e gravar em transações separadas (ou sem transação) é o que faz a busca devolver vazio durante a reingestão. Aplique `Added`/`Updated` primeiro e só então `Removed`, dentro da mesma transação — com a transação a janela já não existe, e a ordem mantém o código correto se um dia ela for partida.
224
228
 
225
229
  ### Chave duplicada no `incoming` é erro de entrada
226
230
 
@@ -308,4 +312,4 @@ ALTER TABLE kb_embeddings ENABLE ROW LEVEL SECURITY;
308
312
 
309
313
  ---
310
314
 
311
- *MORPH-SPEC by Polymorphism Tech — ai-agents/rag-custom-pgvector.md v2.0 (2026-09-08)*
315
+ *MORPH-SPEC by Polymorphism Tech — ai-agents/rag-custom-pgvector.md v2.1 (2026-09-15 — errata da janela no GHLBrain; ressalva da chave por hash)*
@@ -12,7 +12,8 @@
12
12
  ## Fronteira com o testing genérico
13
13
 
14
14
  `backend-dotnet-testing` continua dono do que é de teste em .NET: xUnit, `FakeTimeProvider`,
15
- isolamento entre casos, nomes de teste, organização de fixtures. **Nada disso se repete aqui.**
15
+ isolamento entre casos, nomes de teste, organização de fixtures e o orçamento de teste por task.
16
+ **Nada disso se repete aqui.**
16
17
 
17
18
  Este standard cobre **só o que é de agente** — e o que é de agente cabe em uma frase: o único ponto
18
19
  em que o seu código toca um provider é um `IChatClient`. Todo o resto deste arquivo é consequência
@@ -0,0 +1,64 @@
1
+ # Comentários no código
2
+
3
+ > **Scope:** universal
4
+ > **Layer:** 0 (always load)
5
+ > **Keywords:** comentário, comentario, comment, docstring, jsdoc, xml doc, história da task, código autoexplicativo
6
+ > **Load When:** escrevendo ou revisando código em qualquer stack
7
+
8
+ **Verified against:** C#, TypeScript e JavaScript (convenção de linguagem, sem pacote externo). Last-verified: 2026-09-24.
9
+
10
+ ---
11
+
12
+ Comentário é relido a cada leitura do arquivo, por quem mantém e pelo agente que abre o arquivo na
13
+ próxima task. Ele só se paga quando diz o que o código **não consegue** dizer.
14
+
15
+ ## O que merece comentário
16
+
17
+ Só o **porquê que o código não mostra**:
18
+
19
+ | Merece | Exemplo |
20
+ |---|---|
21
+ | Invariante que o tipo não expressa | "a chave é a identidade e o hash é a versão" |
22
+ | Ordem que importa | "invalidar o cache ANTES de publicar: o consumidor relê na hora" |
23
+ | Peculiaridade externa | API de terceiro que devolve 200 com erro no corpo; bug de biblioteca, com o link |
24
+ | Contrato de API pública | `///` ou JSDoc no membro público: o que recebe, devolve e lança |
25
+
26
+ ## O que não merece
27
+
28
+ - **História de processo.** Task, UC, ADR, FR, Gate, avaliador, `spec.md`, mockup, "confirmado ao
29
+ implementar a T11", "o Gate 3 pediu". Isso vai na mensagem de commit e no `decisions.md`, onde tem
30
+ data e autor e não envelhece dentro do arquivo.
31
+ - **Repetição do código.** `// incrementa o contador` sobre `count++`. Nome bom substitui
32
+ comentário: extraia `isEligibleForRefund()` em vez de comentar a condição.
33
+ - **Estado do mundo fora do arquivo.** "Public Sans é a fonte da interface" passa a mentir na
34
+ primeira troca de fonte, e nada avisa.
35
+ - **Afirmação sobre comportamento.** "nunca retorna vazio", "ordenado por sobrenome": isso é
36
+ asserção de teste. A prosa não falha quando deixa de ser verdade; o teste falha.
37
+
38
+ ## Tamanho
39
+
40
+ Teto de ~3 linhas. Acima disso o comentário justifica o volume (algoritmo não óbvio, armadilha de
41
+ terceiro com a reprodução). Bloco longo narrando decisões é `decisions.md` com outro nome.
42
+
43
+ ## Exemplo
44
+
45
+ ```ts
46
+ // Ruim: conta a task, repete o código e afirma comportamento que nenhum teste cobra.
47
+ // T8 + T11: mutação obrigatória do relatório, item c. Confirmado ao implementar a T11.
48
+ // Faz upsert do documento com chave e hash e nunca duplica.
49
+ await upsert({ key: doc.sourceId, hash: sha256(doc.body) });
50
+
51
+ // Bom: o porquê que o código não mostra.
52
+ // A chave é a identidade e o hash é a versão: hash novo na mesma chave substitui.
53
+ await upsert({ key: doc.sourceId, hash: sha256(doc.body) });
54
+ ```
55
+
56
+ ## Na revisão
57
+
58
+ Comentário de processo é achado da rubrica `code-quality` no Gate 3. O nó `validators` do
59
+ `morph-spec verify` mede, nas linhas que a feature adicionou, quantas linhas de comentário citam
60
+ processo (validador `code-budget`) e avisa com o número.
61
+
62
+ ---
63
+
64
+ *MORPH-SPEC by Polymorphism Tech*
@@ -113,6 +113,28 @@ respectively and the test suite stays green.
113
113
 
114
114
  ---
115
115
 
116
+ ## Test budget
117
+
118
+ The plan (`morph-plan`) gives each task a budget: **one test per postcondition, plus one
119
+ regression test per bug fixed**. Going above it takes a one-line justification in the task.
120
+
121
+ - **Table before copy.** Scenarios of the same behavior — including its edge cases (null, empty,
122
+ boundary) — are rows of one `[Theory]` (`[InlineData]`/`[MemberData]`), never N `[Fact]` methods
123
+ that differ by one value.
124
+ - **No test that only pins text.** An assertion that is only the presence or absence of a string in
125
+ UI copy, tenant config or prose proves the file's version, not behavior. Enforce it with an
126
+ analyzer, a lint rule or a validation at publish time, outside the suite count. The versioned
127
+ prompt case (at most one structural test) is `ai-agents-testing-ai`.
128
+ - **Shared fixture by default.** Reuse the project's `WebApplicationFactory` fixture, fakes and
129
+ builders. Before writing a new helper, look for the existing one (`morph-spec graph explain
130
+ <Name>`; fallback without a graph: `grep -rn`).
131
+ - **TDD where it pays:** a bug gets its regression test first (red, then the fix), and so does pure
132
+ logic. Integration and endpoint tests may come after the code.
133
+ - **A surviving mutant** of the Gate 3 mutation proof is killed by tightening an existing assertion
134
+ or adding a table row, not by one new test per mutant.
135
+
136
+ ---
137
+
116
138
  ## Anti-Patterns
117
139
 
118
140
  | Do NOT | Do Instead |