@polymorphism-tech/morph-spec 8.44.0 → 8.46.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 (199) hide show
  1. package/CLAUDE.md +11 -9
  2. package/README.md +10 -10
  3. package/bin/morph-spec.js +11 -13
  4. package/bin/validate.js +379 -379
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +2 -4
  7. package/docs/QUICKSTART.md +6 -6
  8. package/framework/CLAUDE.md +6 -8
  9. package/framework/MORPH.md +39 -45
  10. package/framework/agents.json +8 -9
  11. package/framework/commands/morph-apply.md +1 -3
  12. package/framework/commands/morph-hotfix.md +1 -1
  13. package/framework/commands/morph-proposal.md +2 -2
  14. package/framework/docs/CLI.md +15 -14
  15. package/framework/evals/review-checklist.md +90 -0
  16. package/framework/evals/trace-schema.md +2 -12
  17. package/framework/hooks/README.md +2 -3
  18. package/framework/hooks/claude-code/core/heavy-gate.js +1 -1
  19. package/framework/hooks/claude-code/core/protect-spec-files.js +2 -2
  20. package/framework/hooks/claude-code/core/telemetry-log.js +2 -2
  21. package/framework/hooks/claude-code/core/trace-autogen.js +1 -22
  22. package/framework/hooks/claude-code/core/validate-completion.js +3 -56
  23. package/framework/hooks/claude-code/statusline.sh +11 -11
  24. package/framework/hooks/git/commit-msg/conventional-commits.sh +33 -33
  25. package/framework/hooks/git/pre-commit/agents.sh +25 -25
  26. package/framework/hooks/git/pre-commit/orchestrator.sh +64 -64
  27. package/framework/hooks/git/pre-commit/specs.sh +50 -50
  28. package/framework/hooks/git/pre-push/run-tests.sh +44 -44
  29. package/framework/hooks/shared/build-check.js +1 -1
  30. package/framework/hooks/shared/comment-scanner.js +1 -1
  31. package/framework/hooks/shared/phase-utils.js +0 -5
  32. package/framework/hooks/shared/stdin-reader.js +26 -26
  33. package/framework/phases.json +4 -6
  34. package/framework/rubrics/backend.json +1 -1
  35. package/framework/rubrics/default.json +1 -1
  36. package/framework/rubrics/frontend.json +1 -1
  37. package/framework/rules/morph-workflow.md +6 -6
  38. package/framework/schemas/config.schema.json +4 -0
  39. package/framework/schemas/feature.schema.json +5 -29
  40. package/framework/schemas/tasks.schema.json +6 -2
  41. package/framework/skills/README.md +1 -2
  42. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  43. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  44. package/framework/skills/core/morph-apply/SKILL.md +145 -246
  45. package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
  46. package/framework/skills/core/morph-hotfix/SKILL.md +5 -3
  47. package/framework/skills/core/morph-implement/SKILL.md +101 -201
  48. package/framework/skills/core/morph-machine/SKILL.md +3 -3
  49. package/framework/skills/core/morph-media/SKILL.md +1 -1
  50. package/framework/skills/core/morph-plan/SKILL.md +171 -287
  51. package/framework/skills/core/morph-proposal/SKILL.md +4 -5
  52. package/framework/skills/core/morph-review/SKILL.md +122 -430
  53. package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
  54. package/framework/skills/core/morph-standards/SKILL.md +2 -2
  55. package/framework/skills/core/morph-uiux/SKILL.md +2 -3
  56. package/framework/standards/STANDARDS.json +10 -10
  57. package/framework/standards/ai-agents/agent-archetypes.md +1 -1
  58. package/framework/standards/ai-agents/agent-session.md +1 -1
  59. package/framework/standards/ai-agents/agent-spec.md +1 -1
  60. package/framework/standards/ai-agents/batching.md +1 -1
  61. package/framework/standards/ai-agents/context-providers.md +1 -1
  62. package/framework/standards/ai-agents/conversational-agent-with-phases.md +1 -1
  63. package/framework/standards/ai-agents/cost-and-budget.md +1 -1
  64. package/framework/standards/ai-agents/durable-workflows-hitl.md +1 -1
  65. package/framework/standards/ai-agents/guardrails.md +1 -1
  66. package/framework/standards/ai-agents/llm-runtime-defaults.md +1 -1
  67. package/framework/standards/ai-agents/mcp-server.md +1 -1
  68. package/framework/standards/ai-agents/mcp-tools.md +1 -1
  69. package/framework/standards/ai-agents/media-pipeline.md +1 -1
  70. package/framework/standards/ai-agents/media-video.md +1 -1
  71. package/framework/standards/ai-agents/middleware-patterns.md +1 -1
  72. package/framework/standards/ai-agents/modalities-image-gen.md +1 -1
  73. package/framework/standards/ai-agents/modalities-speech-to-text.md +1 -1
  74. package/framework/standards/ai-agents/modalities-text-to-speech.md +1 -1
  75. package/framework/standards/ai-agents/modalities-vision.md +1 -1
  76. package/framework/standards/ai-agents/multi-agent-patterns.md +1 -1
  77. package/framework/standards/ai-agents/observability-patterns.md +3 -3
  78. package/framework/standards/ai-agents/production.md +2 -2
  79. package/framework/standards/ai-agents/prompt-sources.md +1 -1
  80. package/framework/standards/ai-agents/providers/model-registry.md +1 -1
  81. package/framework/standards/ai-agents/rag-custom-pgvector.md +1 -1
  82. package/framework/standards/ai-agents/service-tier-flex.md +1 -1
  83. package/framework/standards/ai-agents/setup.md +1 -1
  84. package/framework/standards/ai-agents/structured-output.md +1 -1
  85. package/framework/standards/ai-agents/sweet-spot.md +1 -1
  86. package/framework/standards/ai-agents/testing-ai.md +1 -1
  87. package/framework/standards/ai-agents/tool-adapters.md +1 -1
  88. package/framework/standards/ai-agents/vector-data-extensions.md +1 -1
  89. package/framework/standards/ai-agents/workflows.md +1 -1
  90. package/framework/templates/CLAUDE.md.core.template +1 -1
  91. package/framework/templates/IDE-SNIPPETS.md +262 -262
  92. package/framework/templates/README.md +1 -1
  93. package/framework/templates/REGISTRY.json +0 -81
  94. package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
  95. package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -21
  96. package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
  97. package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
  98. package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
  99. package/framework/templates/docs/spec.md +19 -205
  100. package/framework/templates/docs/ui-components.md +52 -52
  101. package/framework/templates/docs/ui-design-system.md +76 -76
  102. package/framework/templates/docs/ui-flows.md +167 -167
  103. package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
  104. package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
  105. package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
  106. package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
  107. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  108. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  109. package/framework/templates/infrastructure/github/README.md +56 -56
  110. package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
  111. package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
  112. package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
  113. package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
  114. package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
  115. package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
  116. package/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -95
  117. package/framework/templates/state.template.json +17 -17
  118. package/framework/templates/ui/design-system.css +226 -226
  119. package/package.json +2 -2
  120. package/src/commands/e2e/e2e.js +1 -1
  121. package/src/commands/project/heavy.js +1 -1
  122. package/src/commands/project/status.js +3 -3
  123. package/src/commands/state/approve.js +7 -49
  124. package/src/commands/state/gate-check.js +16 -106
  125. package/src/commands/state/gate-decision.js +1 -1
  126. package/src/commands/state/report-check.js +42 -17
  127. package/src/commands/validation/mutate.js +1 -1
  128. package/src/commands/validation/scout-check.js +28 -0
  129. package/src/commands/validation/validate-feature.js +5 -32
  130. package/src/commands/validation/verify.js +98 -48
  131. package/src/core/paths/output-schema.js +0 -2
  132. package/src/core/state/state-manager.js +1 -2
  133. package/src/core/templates/template-renderer.js +477 -477
  134. package/src/dashboard/app/lib/format.js +5 -0
  135. package/src/dashboard/app/main.jsx +13 -13
  136. package/src/dashboard/app/pages/FeatureDetail.jsx +16 -22
  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/Rubrics.jsx +7 -11
  140. package/src/dashboard/scanner/docs-scanner.js +1 -2
  141. package/src/dashboard/scanner/features-scanner.js +3 -4
  142. package/src/dashboard/scanner/rubrics-scanner.js +12 -12
  143. package/src/dashboard/vite.config.js +24 -24
  144. package/src/lib/agents/output-collector.js +0 -1
  145. package/src/lib/ci/ci-verify.js +118 -0
  146. package/src/lib/ci/github-ci.js +192 -0
  147. package/src/lib/ci/workflow-scaffold.js +122 -0
  148. package/src/lib/detectors/design-system-detector.js +188 -188
  149. package/src/lib/evaluator-report.js +52 -334
  150. package/src/lib/generators/design-system-generator.js +508 -508
  151. package/src/lib/generators/handoff-generator.js +4 -5
  152. package/src/lib/installers/memory-installer.js +2 -2
  153. package/src/lib/mutation/manifest.js +8 -2
  154. package/src/lib/mutation/report.js +5 -3
  155. package/src/lib/mutation/runner.js +9 -7
  156. package/src/lib/risk-detector.js +60 -122
  157. package/src/lib/scout/scout-check.js +293 -0
  158. package/src/lib/tasks/task-classifier.js +30 -30
  159. package/src/lib/tasks/task-parser.js +209 -209
  160. package/src/lib/tasks/tasks-json-parser.js +7 -5
  161. package/src/lib/tasks/test-nodes.js +1 -1
  162. package/src/lib/tasks/test-runner.js +1 -1
  163. package/src/lib/telemetry/verify-event.js +3 -2
  164. package/src/lib/validators/css/css-validator.js +340 -340
  165. package/src/lib/validators/reuse/reuse-validator.js +6 -8
  166. package/src/lib/validators/verify-runner.js +1 -1
  167. package/src/scripts/setup-infra.js +1 -1
  168. package/src/utils/agent-id-mapper.js +11 -11
  169. package/src/utils/agents-installer.js +5 -9
  170. package/src/utils/claude-settings-manager.js +300 -300
  171. package/src/utils/hooks-installer.js +3 -10
  172. package/src/utils/skills-installer.js +1 -1
  173. package/framework/evals/proof-rubrics/README.md +0 -76
  174. package/framework/evals/proof-rubrics/eval-de-modelo.md +0 -80
  175. package/framework/evals/proof-rubrics/fiacao.md +0 -54
  176. package/framework/evals/proof-rubrics/guard-cego.md +0 -52
  177. package/framework/evals/proof-rubrics/media-claims.md +0 -109
  178. package/framework/evals/proof-rubrics/mutation-proof.md +0 -107
  179. package/framework/evals/proof-rubrics/observabilidade-genai.md +0 -119
  180. package/framework/evals/proof-rubrics/verde-nao-e-coberto.md +0 -48
  181. package/framework/evals/rubrics/README.md +0 -45
  182. package/framework/evals/rubrics/architecture.md +0 -103
  183. package/framework/evals/rubrics/code-quality.md +0 -98
  184. package/framework/evals/rubrics/condensed.md +0 -57
  185. package/framework/evals/rubrics/contracts.md +0 -67
  186. package/framework/evals/rubrics/test-coverage.md +0 -99
  187. package/framework/evals/scenarios/dotnet-crud.yaml +0 -42
  188. package/framework/evals/scenarios/nextjs-auth.yaml +0 -38
  189. package/framework/evals/suites/standard.yaml +0 -5
  190. package/framework/hooks/claude-code/__pycache__/statusline.cpython-313.pyc +0 -0
  191. package/framework/hooks/claude-code/core/post-edit-typecheck.js +0 -127
  192. package/framework/skills/core/morph-eval/SKILL.md +0 -64
  193. package/framework/templates/.idea/morph-templates.xml +0 -92
  194. package/framework/templates/.vscode/morph-templates.code-snippets +0 -186
  195. package/framework/templates/docs/clarifications.md +0 -253
  196. package/framework/templates/docs/onboarding.md +0 -122
  197. package/framework/templates/docs/schema-analysis.md +0 -119
  198. package/framework/templates/docs/user-stories.md +0 -34
  199. package/src/commands/state/score.js +0 -147
@@ -1,76 +0,0 @@
1
- # Rubricas de prova (`proof-rubrics/`)
2
-
3
- Sete rubricas pontuáveis **0-10** que o avaliador independente do **Gate 3** aplica sobre a
4
- EVIDÊNCIA que ele mesmo produziu — não sobre o produto.
5
-
6
- **Elas não são dimensões do score composto.** O composto continua sendo
7
- `arquitetura + contratos + qualidade + testes`, com os pesos declarados em `../rubrics/`.
8
-
9
- São de dois tipos, e a diferença está em **onde** cada uma impõe teto:
10
-
11
- | Tipo | Quando se aplica | Onde impõe teto |
12
- |---|---|---|
13
- | **Universais** — `mutation-proof`, `verde-nao-e-coberto`, `fiacao`, `guard-cego`, `eval-de-modelo` | sempre | *Cobertura de Testes* (`../rubrics/test-coverage.md`, seção "Tetos de prova (Gate 3)"). O **menor das cinco** é o que vale — todas perguntam a mesma coisa: *a evidência é real?* |
14
- | **Condicional** — `media-claims` | só em feature que **gera mídia** | *Arquitetura* (`../rubrics/architecture.md`, seção "Tetos de prova de mídia"). Entra **sozinha**, fora do menor-das-cinco |
15
- | **Condicional** — `observabilidade-genai` | só em feature que **chama LLM**. Duas etapas, e ela **não** se declara determinística: filtro mecânico de alta recall (o mandate cita um standard `ai-agents`) **+** determinação escrita pelo avaliador, com o motivo. `not_evaluated` sem motivo não é dispensa | *Qualidade de Código* (`../rubrics/code-quality.md`, seção "Teto de prova de observabilidade GenAI"). Entra **sozinha**, fora do menor-das-cinco |
16
-
17
- As duas condicionais ficam **fora** do mínimo de propósito: as afirmações que elas cobram são
18
- **propriedades estruturais** — onde a chamada mora, que estados existem, o que é persistido, o que é
19
- instrumentado —, não perguntas sobre a evidência ser real. Somá-las faria um `media=4` ou um `obs=4`
20
- derrubar o teto de TESTE por um motivo que não tem nada a ver com teste.
21
-
22
- ## Por que num diretório próprio
23
-
24
- `scripts/check-rubric-parity.js` varre **todo** `.md` de `../rubrics/` (exceto `README.md` e
25
- `condensed.md`) e exige o H1 `# Rubrica: {Nome} (Peso {N}%)`, com a soma dos pesos em 100. Um
26
- arquivo destes largado lá reprova o `npm run validate` com um erro de extração — e "consertar"
27
- dando-lhe um peso obrigaria a rebalancear os quatro pesos em oito lugares, mudando o significado de
28
- todo score histórico da base.
29
-
30
- Por isso o H1 daqui é deliberadamente **`# Prova: {Nome}`**: se alguém mover um destes arquivos
31
- para `../rubrics/`, o erro é imediato e nomeado, nunca um desvio silencioso de peso.
32
-
33
- ## Consumidores (cada rubrica nasce com dono)
34
-
35
- | Rubrica | Quem lê | Quem cobra |
36
- |---|---|---|
37
- | `mutation-proof.md` | prompt de dispatch do avaliador — `skills/core/morph-review/SKILL.md` §4 | `src/lib/risk-detector.js` (Gate 3 pausa sem a seção `## Mutações`) + teto em `../rubrics/test-coverage.md` |
38
- | `verde-nao-e-coberto.md` | prompt de dispatch do avaliador — `morph-review` §4 | teto em `../rubrics/test-coverage.md` |
39
- | `fiacao.md` | prompt de dispatch do avaliador — `morph-review` §4 | teto em `../rubrics/test-coverage.md`; cita `verify` `nodes.tests.runs[].count` por `id` |
40
- | `guard-cego.md` | prompt de dispatch do avaliador — `morph-review` §4 | teto em `../rubrics/test-coverage.md` |
41
- | `eval-de-modelo.md` | prompt de dispatch do avaliador — `morph-review` §4 | teto em `../rubrics/test-coverage.md`; a superfície que build e teste unitário não leem |
42
- | `media-claims.md` | prompt de dispatch do avaliador — `morph-review` §4, **só quando a feature gera mídia** | `src/lib/risk-detector.js` (Gate 3 pausa sem a seção `## Mídia gerada`) + teto em `../rubrics/architecture.md` |
43
- | `observabilidade-genai.md` | prompt de dispatch do avaliador — `morph-review` §4, sob a condição de aplicabilidade | teto em `../rubrics/code-quality.md`; a régua da propriedade está em `standards/ai-agents/observability-patterns.md` |
44
-
45
- `test/framework/proof-rubrics.test.js` é o canário: nenhuma rubrica de prova pode ficar **órfã**
46
- (citada por ninguém), nem migrar para o diretório de dimensões.
47
-
48
- ## Como o avaliador reporta
49
-
50
- Duas coisas, no `4-review/evaluator-report.md`:
51
-
52
- 1. A seção `## Mutações` — obrigatória quando a feature tem código de produção (ao menos uma task
53
- com `group` diferente de `docs`). Sem ela, o `morph-spec gate-check {feature} review` **pausa**.
54
- 2. A seção `## Mídia gerada` — obrigatória **só** quando a feature gera mídia (ao menos uma task
55
- cujo `standards[]` cite um dos três standards de mídia). Sem ela, o `gate-check` também **pausa**;
56
- feature que não gera mídia é isenta automaticamente, sem ninguém precisar lembrar.
57
- 3. A linha machine-readable, no mesmo estilo do `**Composite-Score:**`:
58
-
59
- ```
60
- **Proof-Scores:** mutation=9 verde=8 fiacao=9 guard=7 eval=8 media=9
61
- ```
62
-
63
- E, quando a feature chama LLM, a mesma linha carrega também a chave `obs`:
64
-
65
- ```
66
- **Proof-Scores:** mutation=9 verde=8 fiacao=9 guard=7 obs=8
67
- ```
68
-
69
- O **menor** de `mutation`/`verde`/`fiacao`/`guard`/`eval` é o que impõe o teto em Cobertura —
70
- as cinco perguntam a mesma coisa: *a evidência é real?*. Chave ausente não vira zero: o mínimo é
71
- tirado sobre as chaves PRESENTES, para que um relatório antigo de quatro chaves continue legível.
72
-
73
- As chaves `media` e `obs` são lidas **à parte** e não entram naquele mínimo: `media` limita
74
- **Arquitetura** e só aparece quando a feature gera mídia; `obs` limita **Qualidade de Código** e só
75
- aparece quando a feature chama LLM. Ausente a chave (ou com `obs=not_evaluated`), não há teto
76
- daquela família a aplicar.
@@ -1,80 +0,0 @@
1
- # Prova: Eval de Modelo
2
-
3
- > Escopo: Gate 3 (avaliador independente). Não é dimensão do score composto — impõe **teto** em
4
- > Cobertura de Testes (`evals/rubrics/test-coverage.md`, seção "Tetos de prova").
5
-
6
- ## O que esta prova responde
7
-
8
- Prompt e modelo são a única parte do produto cujo comportamento **nem o build nem o teste unitário
9
- leem**: os dois continuam verdes com o prompt inteiro trocado. O eval existe para cobrir
10
- exatamente essa superfície — e é por isso que ele é o artefato que mais facilmente vira teatro.
11
-
12
- A pergunta NÃO é "o eval rodou?", nem "quantos cenários passaram?". É:
13
-
14
- **o que este eval reprovaria, que hoje ele aprova?**
15
-
16
- Um eval que não sabe responder isso mede **execução**, não **detecção** — e a diferença entre as
17
- duas é o defeito inteiro que esta classe de rubrica persegue.
18
-
19
- ## Bandas 0-10
20
-
21
- | 0-3 | 4-6 | 7-8 | 9-10 |
22
- |---|---|---|---|
23
- | Superfície de prompt/modelo mexida sem eval nenhum. Ou eval cujo verde significa "executou": conta cenários e não julga resposta. Miss de cache lido como `skip` | O eval julga a resposta, mas toda asserção passa com qualquer resposta plausível (`Contains` de palavra que o próprio prompt já carrega, "não vazio", "é JSON"). Nenhuma resposta degradada foi injetada — o eval nunca foi visto vermelho | Resposta degradada injetada e o eval ficou vermelho, mas num cenário só; ou quem mordeu foi a asserção de FORMA (o JSON tem a chave) e não a de CONTEÚDO; ou a árvore de cache não tem canário provando que o git a enxerga | Degradação injetada **por cláusula de asserção**, cada uma derrubando o cenário correspondente e **nomeando-o**; contagem de cenários pinada contra encolhimento silencioso; a cache provada legível pelo git; miss é `fail` nomeado, jamais `skip` |
24
-
25
- ## Anti-padrões literais
26
-
27
- - **`Invocations = 0` como prova de qualidade.** Ele prova que **nenhuma chamada paga aconteceu** —
28
- não que o eval reprovaria um modelo pior. Contador de chamadas mede execução; detecção só se mede
29
- **degradando a resposta** e exigindo o vermelho. Medido nesta epic: uma suíte com
30
- `Invocations = 0`, cache quente e 4 de 4 cenários verdes não dizia uma palavra sobre morder.
31
- - **Miss de cache lido como `skip`.** `skip` é lido como "não se aplica" e soma verde no agregado.
32
- Cenário não avaliado é **`fail` nomeado**, com o nome do cenário e o comando que regrava.
33
- - **Asserção que qualquer resposta plausível satisfaz** — `Contains("orçamento")` num cenário cujo
34
- prompt já contém a palavra. A régua tem de separar a resposta boa da ruim, não a resposta da
35
- ausência de resposta. Teste a régua contra a resposta ERRADA, não só contra a certa.
36
- - **O guarda que não enxerga o objeto** (cross-ref `guard-cego.md`). Caso real desta base: o
37
- `git diff --exit-code` sobre a árvore de cache reportava **limpo** — não porque a árvore
38
- estivesse limpa, mas porque os caminhos estouravam o `MAX_PATH` e o git não conseguia **abrir**
39
- os diretórios. Ele avisou no stderr, commitou **1 de 9** arquivos e saiu com código 0. Antes de
40
- acreditar em qualquer canário sobre a cache, prove que ele **enxerga** os arquivos.
41
- - **Dataset que encolhe em silêncio.** Uma suíte que passa a avaliar menos cenários é a mesma classe
42
- de defeito que uma baseline de teste caindo — e ninguém percebe, porque o número que aparece é o
43
- de aprovados.
44
- - **Juiz é o modelo avaliado.** Auto-avaliação do mesmo modelo mede concordância consigo, não
45
- qualidade. Vale para o avaliador LLM da suíte tanto quanto vale para o Gate 3.
46
- - **Regravar a cache para o eval passar.** Regravar é a resposta certa quando **o pin mudou**, e a
47
- errada quando **a asserção começou a reprovar**. As duas situações produzem o mesmo comando e
48
- vereditos opostos: diga qual das duas era, e como sabe.
49
-
50
- ## Insumo: 3-implement/evidencias/
51
-
52
- Task cujo `doneCriteria` julga saída de LLM grava `.morph/features/{feature}/3-implement/evidencias/{taskId}.md`
53
- (`morph-plan` §5): critério, nó `evals` e cenário, modelo, versão do prompt, entrada, saída e
54
- veredito. Não é o `3-implement/reports/{taskId}.md` — aquele é o que o sub-agente diz que fez. É
55
- insumo, não veredito: leia antes de pontuar e confronte o veredito com a saída registrada.
56
-
57
- - **Evidência atada a um nó `evals`** (`runner: "evals"`): as bandas acima valem como estão, e a
58
- degradação desta rubrica usa **o cenário que a evidência aponta** — é ele que tem de cair, pelo
59
- nome.
60
- - **Evidência sem nó** ("nenhum nó — execução manual"): prova que o prompt rodou **uma vez**, não que
61
- algo pegaria a regressão — não há cenário a degradar. Banda **4-6** no máximo, por melhor que a
62
- saída registrada pareça.
63
- - **Evidência devida e ausente** (o path está em `outputs` e o arquivo não existe): superfície de
64
- prompt/modelo mexida sem eval nenhum — banda 0-3.
65
-
66
- ## Como registrar no relatório
67
-
68
- Uma linha por cláusula degradada, dizendo o que foi degradado e qual cenário caiu **pelo nome**:
69
-
70
- ```markdown
71
- - `triagem.acme.a-saudacao` — degradei a resposta EM CACHE trocando a saudação por texto vazio:
72
- `Contains` derrubou o cenário nomeando-o (`eval-fail: triagem.acme.a-saudacao`). Restaurado por
73
- cópia de bytes feita antes; md5 confere.
74
- - `orcamento.acme.a-resposta-estruturada` — removi a chave `total` do payload: `JsonHasKey` mordeu;
75
- `NoPii` continuou verde, como esperado (cláusulas independentes).
76
- - Cache: 9 de 9 arquivos endereçáveis por `git ls-files` (canário `eval-cache-portability`), pior
77
- caminho relativo 145 contra teto 160.
78
- ```
79
-
80
- E no `**Proof-Scores:**`, a chave `eval`.
@@ -1,54 +0,0 @@
1
- # Prova: Fiação
2
-
3
- > Escopo: Gate 3 (avaliador independente). Não é dimensão do score composto — impõe **teto** em
4
- > Cobertura de Testes (`evals/rubrics/test-coverage.md`, seção "Tetos de prova").
5
-
6
- ## O que esta prova responde
7
-
8
- A camada pura pode estar perfeita e o produto continuar quebrado, porque **ninguém ligou o fio**.
9
- Mutar a função pura prova a função; não prova que o comando a chama, que o handler a expõe, nem que
10
- o efeito é observável na saída.
11
-
12
- A pergunta é: **existe um teste que vai da ENTRADA REAL ao EFEITO OBSERVÁVEL, e o inventário de
13
- consumidores do eixo novo está completo?**
14
-
15
- ## Bandas 0-10
16
-
17
- | 0-3 | 4-6 | 7-8 | 9-10 |
18
- |---|---|---|---|
19
- | Só testes de unidade da camada pura. Nenhum consumidor inventariado. Contagem de testes citada como total agregado, escondendo a perna que não rodou | Um teste de ponta a ponta existe, mas só de RECUSA — o caminho de sucesso segue intestável. Inventário por literal, que não vê argumento de helper | Um teste por comando/handler da entrada ao efeito. Inventário por `grep` presente mas incompleto (falta ação em massa, ou estado vazio, ou "limpar", ou chave de cache) | Entrada real → efeito observável em cada comando/handler; inventário de consumidores fechado, um a um; contagem de testes citada por `id` de nó, nunca agregada |
20
-
21
- ## Anti-padrões literais
22
-
23
- - **Mutar a camada pura e concluir que o fix está ligado** — a mutação prova o módulo, nunca a
24
- fiação. Prove o fio com um teste que entra pela superfície real.
25
- - **Só testes de recusa** — eles passam com a escrita quebrada. Medido: um caminho de escrita cujo
26
- corpo virou `return 0;` passou **23 de 23** testes, todos de recusa. O caminho de SUCESSO é o que
27
- precisa de teste.
28
- - **Eixo novo no estado sem inventário de consumidores** — um campo novo tem N leitores. Levante-os
29
- por `grep` e percorra os quatro suspeitos de sempre: **ação em massa**, **estado vazio**,
30
- **"limpar"/reset** e **chave de cache**. Um deles fica de fora por descuido, sempre o mesmo.
31
- - **Varredura por literal quando o valor viaja como argumento de helper** — procurar `"Gardim"` não
32
- encontra `Compose("Gardim")`. Medido: **11 call sites** sobraram numa varredura por literal.
33
- Procure pelo helper, não só pelo valor.
34
- - **Campo novo em spec sem os pontos de wiring, um a um** — schema, parser, escritor, leitor,
35
- validador e superfície de exibição. Liste-os e marque cada um.
36
- - **Citar um total agregado de testes** — o `verify` reporta `nodes.tests.runs[]` com um `count`
37
- **por `id`** (contrato de #90/8.35). Cite **por `id`**: `runs[dotnet].count`, `runs[node].count`.
38
- Um total esconde exatamente a perna que não rodou. E `count: null` é **contagem desconhecida**,
39
- nunca zero — um nó com `null` não autoriza nenhuma afirmação numérica sobre ele.
40
-
41
- ## Como registrar no relatório
42
-
43
- Duas coisas, sempre:
44
-
45
- ```markdown
46
- **Fiação verificada:** `POST /leads` → handler → `LeadService.Create` → linha em `leads` (teste
47
- `LeadsEndpointTests:88`, caminho de SUCESSO, não só de recusa).
48
-
49
- **Contagem por nó:** runs[node].count = 2712 · runs[dotnet].count = 41 · runs[e2e].count = null
50
- (desconhecida — o nó pulou, razão `docker is not available`).
51
- ```
52
-
53
- E, quando a feature acrescenta um eixo ao estado, o inventário de consumidores como lista fechada,
54
- com o comando de `grep` que a produziu.
@@ -1,52 +0,0 @@
1
- # Prova: Guard Cego
2
-
3
- > Escopo: Gate 3 (avaliador independente). Não é dimensão do score composto — impõe **teto** em
4
- > Cobertura de Testes (`evals/rubrics/test-coverage.md`, seção "Tetos de prova").
5
-
6
- ## O que esta prova responde
7
-
8
- Um guard só vale contra o ataque que ele consegue VER. O jeito de descobrir o que ele não vê não é
9
- mudar o conteúdo que ele já inspeciona — é mudar a **FORMA** do que chega até ele.
10
-
11
- A pergunta é: **qual entrada válida este guard deixa passar sem enxergar, e o ramo que ele abriu
12
- tem teste próprio?**
13
-
14
- ## Bandas 0-10
15
-
16
- | 0-3 | 4-6 | 7-8 | 9-10 |
17
- |---|---|---|---|
18
- | Guard sem teste próprio. Ou testado só com o conteúdo que ele já rejeita, nunca com forma diferente | Ataque por conteúdo apenas. Guard que compara `Serialize(A) == Serialize(B)` aceito sem cross-check | Ataque por forma feito; falta o cross-check por contador independente, ou o predicado do guard continua mais estreito que a ação que ele governa | Forma atacada (multi-linha, aninhamento, ordem, codificação), cross-check por contador independente, predicado tão largo quanto a ação, e todo ramo recém-alcançável com teste próprio |
19
-
20
- ## Anti-padrões literais
21
-
22
- - **Atacar o guard só pelo conteúdo** — mude a **FORMA**. Medido: o guard era um parser linha a
23
- linha e o YAML **multi-linha** era perfeitamente válido; o conteúdo proibido passou inteiro
24
- porque chegou numa forma que o parser não montava.
25
- - **`Serialize(A) == Serialize(B)` como prova de igualdade** — o guard é cego ao próprio
26
- serializador: se ele perde um campo, perde nos dois lados e a comparação dá igual. Faça
27
- **cross-check por um contador independente** (número de itens, soma, hash de um campo-chave).
28
- - **Predicado mais estreito que a ação que ele governa** — um publish que só olhava o *spec* e
29
- virava no-op sobre tudo o mais. Afirme sobre o **texto publicado**, não sobre a intenção de
30
- publicar.
31
- - **Early-return depois do filtro** — o `return` sai antes da limpeza e o estado fica meio mutado.
32
- Quando o filtro é um `skip`, transforme-o em `else`: o caminho de saída precisa passar pelo
33
- mesmo fecho.
34
- - **Ramo antes inalcançável que passou a ser alcançável** — isso é **código NOVO**, ainda que a
35
- linha seja antiga e o diff não a toque. Ele precisa de teste próprio; nenhuma cobertura anterior
36
- responde por ele.
37
- - **Guard novo sem prova de que morde** — canário que não morde é canário errado, e canário que
38
- morde pelo motivo errado é pior: mute a produção, exija que o teste FALHE **nomeando o que
39
- quebrou**, e restaure por cópia de bytes feita antes.
40
-
41
- ## Como registrar no relatório
42
-
43
- Uma linha por guard tocado, dizendo a forma atacada e o resultado:
44
-
45
- ```markdown
46
- - `chain.js:blockRawWorktreeAdd` — atacado por FORMA: comando com `\` de continuação em duas linhas
47
- e com aspas simples. Bloqueou nas duas. Cross-check: contador de comandos bloqueados subiu 2.
48
- - Ramo `options.discardIgnored === true` era inalcançável antes de T4 — agora é alcançável e ganhou
49
- teste próprio (`finish.test.js:412`).
50
- ```
51
-
52
- E no `**Proof-Scores:**`, a chave `guard`.
@@ -1,109 +0,0 @@
1
- # Prova: Media Claims
2
-
3
- > Escopo: Gate 3 (avaliador independente), **somente** em feature que gera mídia. Não é dimensão
4
- > do score composto — o composto continua `arquitetura + contratos + qualidade + testes`. Esta prova
5
- > impõe **teto** em Arquitetura (tabela em `evals/rubrics/architecture.md`, seção "Tetos de prova de
6
- > mídia").
7
-
8
- ## O que esta prova responde
9
-
10
- Uma feature que gera imagem ou vídeo faz **quatro afirmações** que custam caro quando são falsas, e
11
- as quatro são fáceis de escrever e difíceis de provar: *controlamos o custo*, *tem revisão humana*,
12
- *a mídia é marcada*, *o hot path não bloqueia*.
13
-
14
- A pergunta desta rubrica é sempre a mesma: **onde está o código que torna a afirmação verdadeira,
15
- e o que acontece se alguém tentar burlá-la?**
16
-
17
- Ela vive em Arquitetura, e não em Cobertura, porque as quatro são **propriedades estruturais** —
18
- onde a chamada mora, que estados existem, o que é persistido, o que é publicado. Uma feature que
19
- gera mídia dentro do turno de WhatsApp está arquiteturalmente errada, não mal testada; contá-la como
20
- falha de teste diria a coisa errada a quem lê o relatório.
21
-
22
- As quatro afirmações, com o que conta e o que não conta:
23
-
24
- | Afirmação | Evidência que conta | Evidência que **não** conta |
25
- |---|---|---|
26
- | **Custo** | Custo estimado gravado **antes** da chamada, com a tarifa e a data da tabela de preços; teto por tenant/dia exercitado em teste, com o caso que ESTOURA | "usamos o modelo mais barato"; uma constante de preço sem fonte datada |
27
- | **Revisão humana** | Teste que prova que a publicação falha a partir de **todo** estado que não seja aprovado, e que aprovar carimba quem e quando | Um comentário dizendo que há revisão; um campo booleano `approved` |
28
- | **Marcação** | A publicação **recusa** quando a finalidade exige rótulo e ele não veio — com texto e superfície nomeados, gravados junto do artefato — mais a proveniência persistida e o C2PA preservado sem re-encode. No exemplar: `MediaJob.Publish(MediaDisclosure?, at)`, terceira porta | "o SynthID vem embutido" — vem, e cai no primeiro re-encode ou screenshot; um campo de rótulo que a publicação aceita vazio; a obrigação escrita só no comentário do estado `Published` |
29
- | **Hot path não bloqueado** | O tipo que produz bytes é **inacessível** do handler (teste de acessibilidade por reflexão) **e** o handler de mensagem devolve id de job | "o handler chama o serviço assíncrono"; um `Task.Run` |
30
-
31
- O conhecimento por trás das quatro está em `.morph/framework/standards/ai-agents/media-pipeline.md`.
32
- Esta rubrica não o repete: ela cobra a prova.
33
-
34
- ## Bandas 0-10
35
-
36
- | 0-3 | 4-6 | 7-8 | 9-10 |
37
- |---|---|---|---|
38
- | Nenhuma das quatro afirmações tem código apontado. Ou a geração acontece dentro do turno de conversa, ou a publicação é alcançável sem aprovação humana — qualquer um dos dois é **bloqueante**, independentemente do resto | As quatro estão implementadas, mas a prova é leitura de código: nenhum teste exercita o caminho de recusa. Teto de custo declarado e nunca estourado em teste; marcação prometida na prosa e ausente no ponto de publicação | As quatro provadas por teste, com o caminho de recusa exercitado ao menos uma vez cada. Falta a mutação: nenhuma delas foi vista MORDENDO, ou a proveniência grava menos campos do que o standard nomeia | As quatro provadas, cada caminho de recusa com teste próprio, **e a acessibilidade do tipo que produz bytes asserida por reflexão com a mutação rodada** (tornar público, ver o teste falhar nomeando o defeito, restaurar por bytes e conferir por md5). Custo estimado antes da chamada, com tarifa datada; teto exercitado no caso que estoura. A marcação é **parâmetro da transição de publicação**, não campo opcional: existe o teste que publica sem rótulo e vê a recusa |
39
-
40
- ## Anti-padrões literais
41
-
42
- - **Prender a regra dura só na prosa.** "O standard diz para não gerar no turno" não impede nada. O
43
- que impede é a acessibilidade do tipo: se um handler consegue **nomear** o tipo que produz bytes,
44
- a regra não está presa — mesmo que ninguém a tenha violado ainda. (memória: é a diferença entre
45
- "o standard diz para não fazer" e "o compilador recusa")
46
- - **Testar a fachada em vez do dono do estado.** Se `Review.Publish(job)` reimplementa a regra em
47
- vez de delegar a `job.Publish()`, o teste da fachada mede uma **cópia** do objeto — e as duas
48
- divergem no primeiro conserto. Exercite as duas contra o MESMO job e compare.
49
- - **Confundir a chave de idempotência do PEDIDO com o hash do CONTEÚDO.** Uma dedupe chamada paga, a
50
- outra dedupe storage. Um teste que só confere "o hash é o mesmo" **não distingue dedupe de
51
- regravação**: as duas produzem o mesmo hash. Conte as gravações.
52
- - **Aceitar `estimatedCostUsd` gravado DEPOIS da chamada.** Isso é contabilidade, não controle: o
53
- dinheiro já saiu. A prova é a ordem, e a ordem é verificável.
54
- - **Preço em código sem fonte datada.** Uma constante `0.211m` sem a página e a data de consulta é
55
- folclore com força de orçamento. A tarifa vem da tabela do standard da modalidade.
56
- - **Tratar bloqueio de moderação como erro técnico.** Se o estado de bloqueio for retryable, um laço
57
- de retry vira uma fatura — e no provedor que cobra o bloqueio, cobra duas vezes. Exija dois
58
- estados distintos e um teste por estado.
59
- - **`not_detected` lido como "feito por humano".** Ausência de marca é ausência de informação. Um
60
- verificador de proveniência que devolve "não detectado" não prova origem humana.
61
- - **Marcação delegada ao provedor.** C2PA e SynthID vêm embutidos e **caem** com screenshot ou
62
- re-encode. A marcação visível é obrigação separada, e ela mora no ponto de publicação — não no de
63
- geração.
64
- - **Marcação presa só por prosa.** É o anti-padrão mais fácil de cometer porque parece resolvido: o
65
- comentário do estado `Published` diz "com a marcação aplicada", a finalidade `Marketing` diz
66
- "marcação visível obrigatória", e um `grep` por rótulo, watermark ou disclosure no código volta
67
- **vazio**. Se as outras três afirmações estão presas por tipo ou por estado e a marcação — que é a
68
- obrigação legal — está presa por nada, a assimetria é o achado. Prova mínima: a publicação recusa
69
- sem rótulo.
70
- - **Ler "o tipo prende" como "nada mais é possível".** O `internal` fecha *obter bytes* e *nomear o
71
- produtor*; ele **não** fecha *aguardar inline*. O gatilho do worker (`IMediaJobRunner.RunAsync` no
72
- exemplar) é público por necessidade — quem o dispara é o `BackgroundService` do projeto — e um
73
- `await runner.RunAsync(id)` dentro de um handler de turno **compila** e paga a latência inteira.
74
- Procure por esse `await`: é o que o compilador não procura. Uma prova que só cita a acessibilidade
75
- e não olha os call sites do runner cobriu metade da afirmação.
76
- - **Teste que toca API paga.** Uma chave de API num teste é uma fatura por execução de CI e um teste
77
- que fica vermelho quando o provedor tem incidente. `grep` por chave, host do provedor e leitura de
78
- ambiente nos arquivos de teste tem de voltar vazio.
79
- - **Aceitar o número de testes sem a suíte inteira.** "Derruba 4" medido sobre um arquivo e "derruba
80
- 4 de 203" são afirmações diferentes. Só a suíte inteira autoriza a frase.
81
- - **Declarar as quatro afirmações numa feature que não gera mídia.** A isenção é automática: se
82
- nenhuma task pina um standard de mídia, a seção não é exigida. Escrever a seção "por precaução"
83
- gasta o Gate 3 com cerimônia vazia, que é como uma regra vira ruído e depois é desligada.
84
-
85
- ## Como registrar no relatório
86
-
87
- Na seção `## Mídia gerada` do `evaluator-report.md`, uma linha por afirmação, com os quatro campos:
88
-
89
- ```markdown
90
- ## Mídia gerada
91
-
92
- | Afirmação | Onde está presa (arquivo:linha) | Teste que a prova | Rodada de mutação |
93
- |---|---|---|---|
94
- | Custo | src/…/MediaGenerator.cs:88 (reserva ANTES da chamada) | MediaGeneratorTests.ABudgetOverrun… | — |
95
- | Revisão humana | src/…/MediaJob.cs:… (Publish só de Approved) | MediaJobStateTests.PublishFails… (6 estados) | — |
96
- | Marcação | src/…/MediaJob.cs:… (3ª porta de Publish) + MediaDisclosure.cs | MediaDisclosureTests.PublishFailsWithoutADisclosure… | porta removida → 2 de 220 caem, motivo lido, md5 restaurado |
97
- | Hot path | src/…/IMediaProvider.cs (internal) | MediaAccessibilityTests.TheTypeThat… | público → 4 de 220 caem, motivo lido, md5 restaurado |
98
- ```
99
-
100
- E a linha machine-readable, no mesmo estilo do `**Composite-Score:**`, junto das demais provas:
101
-
102
- ```
103
- **Proof-Scores:** mutation=9 verde=8 fiacao=9 guard=7 media=9
104
- ```
105
-
106
- **A chave `media` só entra quando a feature gera mídia.** Feature que não pina nenhum standard de
107
- mídia não declara a seção e não declara a chave — e o gate não a cobra. Feature que gera mídia **sem
108
- a seção** pausa o Gate 3 por conta própria (`src/lib/risk-detector.js`), antes de qualquer discussão
109
- de nota.
@@ -1,107 +0,0 @@
1
- # Prova: Mutação
2
-
3
- > Escopo: Gate 3 (avaliador independente). Não é dimensão do score composto — o composto continua
4
- > `arquitetura + contratos + qualidade + testes`. Esta prova impõe **teto** em Cobertura de Testes
5
- > (tabela em `evals/rubrics/test-coverage.md`, seção "Tetos de prova").
6
-
7
- ## O que esta prova responde
8
-
9
- Um teste verde prova que o código roda. Só a **mutação** prova que o teste MORDE: quebre de
10
- propósito a linha que carrega a decisão e veja quem grita. Se ninguém grita, a suíte é decoração —
11
- e foi assim que 11 testes verdes cobriram um artefato que o build recusava.
12
-
13
- A pergunta desta rubrica é sempre a mesma: **você mutou o call site que carrega a decisão, provou
14
- cada sobrevivente contra a suíte inteira, leu o MOTIVO da falha, e devolveu o arquivo byte a byte
15
- — com o binário provado?**
16
-
17
- O método é `morph-spec mutate` (manifesto de mutantes → uma chamada → a seção pronta). Você escolhe
18
- os mutantes; a ferramenta escolhe o subconjunto de testes, roda o lote, refaz todo sobrevivente com o
19
- mutante compilado de verdade contra a suíte INTEIRA e reverte por bytes.
20
-
21
- ## Bandas 0-10
22
-
23
- | 0-3 | 4-6 | 7-8 | 9-10 |
24
- |---|---|---|---|
25
- | Nenhuma mutação. Ou mutação declarada sem diff, sem md5 e sem testes nomeados — afirmação, não prova. Ou revert por `git checkout --`, que descarta trabalho não commitado de terceiros | Mutou, mas no lugar errado (o `if` refatorado, não o call site que decide) ou declarou "sobreviveu" / "derruba N" a partir de um filtro escolhido por você ("derrubou 3" medido sobre 40 testes de um arquivo só). Reportou md5 sem ler o motivo da falha | Mutação no call site certo, todo sobrevivente refeito contra a suíte INTEIRA, diff e md5 antes/depois. Falta o MOTIVO literal da falha, ou uma cláusula de um predicado composto ficou sem rodada própria | Uma rodada por cláusula, cada uma com arquivo:linha, diff, md5 antes/depois, contagem com o ESCOPO dito (`≥K de N` no subconjunto que a ferramenta escolheu; `K de M` na suíte inteira) e a mensagem literal de falha. Revert por snapshot de bytes com escrita nova, rebuild forçado e DLL final igual (md5) à de antes do 1º mutante, com `git status --porcelain src/` vazio ao fim |
26
-
27
- ## Anti-padrões literais
28
-
29
- - **Mutar o `if` refatorado em vez do call site que carrega a decisão** — o extract-method moveu a
30
- regra; o teste continua batendo no invólucro. Ancore onde o valor é DECIDIDO. (memória: guard do
31
- remédio, cuja checagem real vivia duas camadas acima do `if` mutado)
32
- - **Reportar "sobreviveu" ou "derruba N" a partir de um filtro que você escolheu** — "3 de 40" e
33
- "3 de 2684" são afirmações diferentes. **"Sobreviveu" só vale contra a suíte INTEIRA**, com o
34
- mutante compilado de verdade. **"Morto" pode sair de um subconjunto, se a ferramenta o escolheu**
35
- (testes que a feature alterou + os que citam o tipo mutado): teste que falha no subconjunto falha
36
- na suíte, então a morte é a mesma — mas a contagem passa a ser piso (`≥K de N (subconjunto)`), e
37
- `K de M` só existe medido na suíte inteira. (memória: F30, filtro no Gate 3)
38
- - **Contar como morto o teste instável** — morte na suíte inteira só vale se a mesma falha se repete
39
- na segunda rodada (dos testes que falharam, isolados, ou da suíte inteira). Medido: um teste de host, sem relação com o mutante,
40
- "derrubou" um mutante uma vez; em sete rodadas o mutante sobreviveu seis. Falha que não se
41
- repete é sobrevivente com razão dita, não morte. (mesma rodada: `out var` removido que só compila
42
- no schemata é `build-error`, não morto)
43
- - **Filtro tão estreito que a prova fica vácua** — mutar e rodar só o teste que você já sabia que
44
- quebraria não testa nada além da sua própria expectativa. O subconjunto é da ferramenta, nunca
45
- seu: escolhido à mão, ele volta a ser a sua expectativa.
46
- - **Aceitar um seed sem perguntar "que sequência real produz este seed?"** — fixture que só existe
47
- no teste prova o teste, não o sistema.
48
- - **Imprimir `diff` + md5 e parar por aí** — md5 prova que ALGO mudou, nunca O QUÊ. Sem a mensagem
49
- literal de falha (`expected 3, got 0` em `x.test.js:88`), a prova é de que o arquivo mudou, não de
50
- que o teste morde a regra. (memória: três rodadas de correção nesta epic)
51
- - **Reverter por replace reverso, ou por `git checkout --`** — o replace reverso erra em arquivo com
52
- ocorrências repetidas; o `git checkout --` **apaga alterações não commitadas de outra pessoa** e
53
- aconteceu de verdade num Gate 3 desta epic. Reverta por **snapshot/restore de bytes** (cópia feita
54
- ANTES), por escrita nova, e confirme por `md5sum`.
55
- - **Devolver o mtime original com `os.utime` depois de restaurar** — os bytes voltam, mas com data
56
- ANTIGA o arquivo fica mais velho que a DLL do último mutante: o MSBuild acha o binário em dia, não
57
- recompila, e a rodada-base roda sobre o mutante (falso vermelho, ou pior: falso verde). Medido num
58
- projeto .NET 10 de 697 arquivos: restore com data antiga → build de 1,9 s, nenhum `CoreCompile`,
59
- DLL do mutante intacta; escrita nova → DLL de volta ao md5 da baseline. Restaure por **escrita
60
- nova**, force o rebuild e **prove que o binário é o do disco** (md5 da DLL == a construída da
61
- árvore limpa antes do 1º mutante).
62
- - **Escrever a mutação por heredoc do shell quando ela contém barra invertida** — o transporte come
63
- o escape: `\b` vira o byte `0x08`, `\n` vira quebra de linha real, e o regex mutado **não é o que
64
- você escreveu**. Sintoma: a mutação parece aplicada e o resultado não muda, ou o arquivo quebra
65
- num ponto que você não tocou. Diagnóstico em um comando: `cat -A` mostra `^H` onde devia estar
66
- `\b`. Medido: o transporte do Bash reduz `\\` a `\` e decodifica escapes unicode, até dentro de
67
- heredoc de aspas simples. Saída: o manifesto de `morph-spec mutate` com a barra invertida do código
68
- escrita como o token `{{BS}}` (a ferramenta a expande), ou patch por **índice de linha** com o
69
- conteúdo montado sem barra invertida no fonte (`String.fromCharCode(92)`) — nunca a barra invertida
70
- crua num heredoc.
71
- (memória: **três agentes** desta epic tropeçaram nisto; dois descartaram o resultado, um só
72
- descobriu ao conferir os bytes)
73
- - **Mutar antes de commitar o fix** — se a sessão morrer no meio, o que sobrevive é a mutação, não a
74
- correção. **Commite o fix ANTES de mutar**, sempre.
75
- - **Ler mais do que a primeira assertiva que falha** — a mutação só prova até ali; o resto do teste
76
- nem rodou. Não conte cobertura que a mutação não exercitou.
77
- - **Uma rodada só para um predicado `A && B`** — derrubar `A` não diz nada sobre `B`. **Uma rodada
78
- por cláusula.** O mesmo vale para um guard de duas classes: toque **cada** classe.
79
- - **Deixar loader, validador e superfície de publicação fora do alvo** — o defeito costuma estar em
80
- quem LÊ o arquivo, não em quem o escreve. (memória: 11 testes verdes contra o render do próprio
81
- gerador, nenhum deles CARREGANDO o artefato)
82
- - **Fixture fora da ordem de grandeza da tese** — tese sobre 2 600 testes provada num fixture de 3.
83
- - **Mutação de prosa presa por regex** — texto de skill/rubrica não se prende por `assert.match` de
84
- vocabulário: mude a FORMA (mova a seção, troque o cabeçalho) e veja se o canário ainda morde.
85
- - **Número de mutação sem SHA e sem data** — "derruba 12" envelhece em uma semana. Anote o SHA
86
- revisado e a data ao lado do número.
87
- - **Terminar sem `git status --porcelain src/` vazio** — mutação esquecida em produção é o pior
88
- desfecho possível deste método, e já aconteceu com avaliador morto no meio da rodada.
89
-
90
- ## Como registrar no relatório
91
-
92
- Na seção `## Mutações` do `evaluator-report.md`, uma linha por mutação, com os cinco campos:
93
-
94
- ```markdown
95
- | # | Arquivo:linha | Mutação (diff) | md5 antes | md5 depois | Testes derrubados | Motivo da falha |
96
- |---|---|---|---|---|---|---|
97
- | M1 | src/lib/x.js:42 | `- if (a && b)` / `+ if (a)` | abc… | def… | ≥3 de 212 (subconjunto) | `expected 3 got 0` em x.test.js:88 |
98
- | M2 | src/lib/y.js:7 | `- return ok` / `+ return true` | 123… | 456… | 0 de 2684 (SOBREVIVEU, suíte inteira) | nenhum teste falhou na suíte inteira |
99
- ```
100
-
101
- A coluna "Testes derrubados" sempre diz o escopo: `≥K de N (subconjunto)` é piso; `K de M (suíte
102
- inteira)` é contagem; sobrevivente é sempre `0 de M (suíte inteira)`. `morph-spec mutate` imprime a
103
- seção inteira nesse formato — cole-a, não a redigite.
104
-
105
- E, ao fim da seção: **Reversão:** snapshot/restore de bytes por escrita nova; DLL final igual (md5)
106
- à de antes do 1º mutante; `git status --porcelain src/` vazio. Feature sem código de produção
107
- declara a seção com a linha `não aplicável — feature sem código de produção`.
@@ -1,119 +0,0 @@
1
- # Prova: Observabilidade GenAI
2
-
3
- > Escopo: Gate 3 (avaliador independente). Não é dimensão do score composto — impõe **teto** em
4
- > Qualidade de Código (`evals/rubrics/code-quality.md`, seção "Teto de prova de observabilidade
5
- > GenAI"). Aplica-se **somente** a feature que chama LLM; feature sem LLM é `not_evaluated`, e
6
- > `not_evaluated` **não** aplica teto.
7
-
8
- ## O que esta prova responde
9
-
10
- Uma feature que chama um modelo gasta dinheiro, pode falhar de formas que nenhum teste cobre, e
11
- produz texto que ninguém revisou. A pergunta que importa depois do incidente é sempre a mesma, e
12
- raramente tem resposta:
13
-
14
- **Dá para reconstruir o que foi ao provedor, e o que ele respondeu, neste turno?**
15
-
16
- Repare no que a pergunta **não** é. Ela não é "tem span OpenTelemetry?". Essa formulação foi medida
17
- contra o acervo e reprova **5 de 5** repositórios — e, pior, daria a mesma nota a um projeto cuja
18
- observabilidade inteira é a mensagem da exceção e a outro que tem hash por bloco de prompt, vocabulário
19
- fechado de desfecho de tool, veredito ternário de guard, sete testes provando que a telemetria é
20
- inerte, e a invariante escrita de que o texto enviado é derivado sem cache *"para a telemetria não
21
- poder mentir"*. **Régua que pune o melhor aluno não é régua.**
22
-
23
- Span com as convenções GenAI é a forma **recomendada** (`ai-agents-observability-patterns`) e alcança
24
- a faixa mais alta. Telemetria própria também alcança — desde que exista **teste de fidelidade**, isto
25
- é, algo que prove que o registrado é o enviado. O que a rubrica cobra é o **resultado**; o standard é
26
- quem recomenda o meio.
27
-
28
- ### Aplicabilidade — filtro mecânico, e depois uma determinação nomeada
29
-
30
- **Esta rubrica NÃO se declara determinística, e a razão é medida:** nenhuma leitura de `tasks.json`
31
- ou do `mandate.md` mede a propriedade que importa — *esta feature entrega código que chama um
32
- modelo?*. A versão anterior deste bloco prometia determinismo e **errou no primeiro cliente que
33
- encontrou**: a própria feature que criou esta rubrica satisfazia as duas condições (tasks com
34
- `group: "tests"`, mandate citando `ai-agents` dez vezes) e não chama LLM nenhum.
35
-
36
- São **duas etapas**, e a segunda não é opcional:
37
-
38
- **1. Filtro mecânico, de alta recall** (barato, lido do disco):
39
-
40
- > o `mandate.md` cita ao menos um standard `ai-agents`.
41
-
42
- Falso ⇒ `not_evaluated`, sem mais discussão. É deliberadamente **largo**: erra para o lado de
43
- convocar a determinação, não para o de dispensá-la.
44
-
45
- **2. Determinação, escrita pelo avaliador.** Passado o filtro, a pergunta é
46
- *a feature entrega código que chama um modelo?* — e o relatório **registra a resposta com o motivo**:
47
-
48
- ```markdown
49
- - Aplicabilidade: LLM SIM — T4 acrescenta `TriagemAgent` sobre `IChatClient` (`AgentCatalog.cs:31`).
50
- - Aplicabilidade: `not_evaluated` — o mandate cita `ai-agents`, mas o diff é 100% markdown de
51
- standard; nenhuma chamada a modelo entra no produto.
52
- ```
53
-
54
- **Sinal auxiliar, não determinação:** toda task com `group` em `{docs, tests}` é indício **contra**
55
- código de produção que chama modelo. Indício, porque um harness de agente pode viver inteiro numa
56
- task `tests` — por isso ele informa a determinação em vez de substituí-la.
57
-
58
- **Um `not_evaluated` sem motivo escrito não é dispensa: é o escape que o standard
59
- `ai-agents-guardrails` proíbe.** O motivo é o que torna a decisão auditável na revisão seguinte, e é o que impede que
60
- `not_evaluated` vire a saída fácil.
61
-
62
- ## Bandas 0-10
63
-
64
- | 0-3 | 4-6 | 7-8 | 9-10 |
65
- |---|---|---|---|
66
- | A feature chama LLM e **nada** permite saber o que foi enviado, quanto custou, ou se falhou. Observabilidade é a mensagem da exceção | Chamada de LLM registrada **só por log de texto**: sem contagem de tokens, sem correlação com a feature ou a requisição, sem forma de amarrar o registro ao turno | Instrumentação presente e correta — mas a evidência no relatório é a **linha de wiring**, não a saída. O avaliador leu o `AddSource`, não leu um span | Span por chamada com `gen_ai.operation.name` e `gen_ai.provider.name`, tokens registrados, correlação com a feature, e sensível desligado fora de dev — **ou** telemetria própria equivalente cuja fidelidade tem teste. Nos dois casos, o relatório **mostra um span ou registro capturado** |
67
-
68
- A fronteira que decide 7-8 × 9-10 é uma só, e é a mesma de toda rubrica desta classe: **evidência
69
- capturada, não evidência declarada.** Ler o código que instrumenta prova que alguém escreveu o
70
- wiring; ler um span prova que ele emite.
71
-
72
- ## Anti-padrões literais
73
-
74
- - **Pontuar o wiring como se fosse a saída** — `builder.AddSource(...)` no diff é 7-8, nunca 9-10.
75
- Para subir, capture: rode o caminho, pegue **um** span ou **uma** linha de registro, e cole no
76
- relatório com os atributos que ela carrega.
77
- - **Aceitar "temos logs"** — log de texto sem tokens e sem correlação é banda 4-6. A pergunta
78
- operacional é "quanto custou este turno e o que foi enviado nele"; um log que não responde nenhuma
79
- das duas não é observabilidade, é rastro.
80
- - **Reprovar telemetria própria por não ser OpenTelemetry** — a rubrica cobra a propriedade. Um
81
- registro próprio com **teste de fidelidade** vale 9-10. Sem teste de fidelidade, não vale: é uma
82
- afirmação sobre o que o código faz, feita pelo mesmo código.
83
- - **Aceitar hash sem a invariante** — um hash do prompt só prova algo se o texto de que ele é hash
84
- **é** o texto enviado. Se existe cache entre a montagem e o envio, telemetria e requisição podem
85
- divergir em silêncio. Procure a derivação sem cache; sem ela, o hash é decoração e a nota não passa
86
- de 6.
87
- - **Prompt cru em produção contado como ponto a favor** — é o oposto: dado sensível persistido é
88
- achado, não evidência. Cru só em dev ou harness, com opt-in; em produção, hash.
89
- - **Confundir "não tem LLM" com "tem e não instrumentou"** — o primeiro é `not_evaluated` e **não**
90
- aplica teto; o segundo é 0-3. Colapsar os dois num booleano é o mesmo defeito que o veredito
91
- ternário de `ai-agents-guardrails` existe para impedir.
92
- - **Aplicar teto sem ter avaliado** — sem a linha da chave `obs` no `**Proof-Scores:**` não há teto a
93
- aplicar. Um teto inventado a partir de impressão é pior que teto nenhum.
94
- - **Pontuar telemetria de turno cancelado como cobertura** — registro de algo que não aconteceu é
95
- achado, não evidência.
96
-
97
- ## Como registrar no relatório
98
-
99
- Uma linha dizendo o que foi capturado e como, e — quando a nota for 9-10 — o artefato capturado:
100
-
101
- ```markdown
102
- - Aplicabilidade: filtro passou (mandate cita `ai-agents-cost-and-budget`); determinação: LLM SIM — T4 acrescenta `TriagemAgent` sobre `IChatClient` (`AgentCatalog.cs:31`).
103
- - Capturado 1 span de `Morph.Agents.Triagem` rodando o caminho real com o fake:
104
- `chat gpt-5-mini` · gen_ai.operation.name=chat · gen_ai.provider.name=openai ·
105
- gen_ai.usage.input_tokens=1841 · output_tokens=212 · morph.feature=triagem-lead
106
- - Fidelidade: `PromptDecompositionTests.TextoEnviadoEhAConcatenacaoDosBlocos` prova que o hash
107
- registrado é o do texto enviado (derivação sem cache).
108
- - EnableSensitiveData=false confirmado fora de dev (`appsettings.Production.json`).
109
- ```
110
-
111
- E no `**Proof-Scores:**`, a chave `obs`.
112
-
113
- Quando não se aplica, uma linha basta — e ela precisa dizer **o que** foi determinado, e por quê. Um `not_evaluated` mudo não é dispensa:
114
-
115
- ```markdown
116
- - Observabilidade GenAI: `not_evaluated` — o filtro passou (o mandate cita `ai-agents`), mas o
117
- diff é 100% markdown de standard: nenhuma chamada a modelo entra no produto. Sem teto sobre
118
- code-quality.
119
- ```