@polymorphism-tech/morph-spec 8.44.0 → 8.45.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.
- package/CLAUDE.md +10 -9
- package/README.md +10 -10
- package/bin/morph-spec.js +10 -13
- package/bin/validate.js +379 -379
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +2 -4
- package/docs/QUICKSTART.md +6 -6
- package/framework/CLAUDE.md +6 -8
- package/framework/MORPH.md +39 -45
- package/framework/agents.json +8 -9
- package/framework/commands/morph-apply.md +1 -3
- package/framework/commands/morph-hotfix.md +1 -1
- package/framework/commands/morph-proposal.md +2 -2
- package/framework/docs/CLI.md +10 -10
- package/framework/evals/review-checklist.md +90 -0
- package/framework/evals/trace-schema.md +2 -12
- package/framework/hooks/README.md +2 -3
- package/framework/hooks/claude-code/core/heavy-gate.js +1 -1
- package/framework/hooks/claude-code/core/protect-spec-files.js +2 -2
- package/framework/hooks/claude-code/core/telemetry-log.js +2 -2
- package/framework/hooks/claude-code/core/trace-autogen.js +1 -22
- package/framework/hooks/claude-code/core/validate-completion.js +3 -56
- package/framework/hooks/claude-code/statusline.sh +11 -11
- package/framework/hooks/git/commit-msg/conventional-commits.sh +33 -33
- package/framework/hooks/git/pre-commit/agents.sh +25 -25
- package/framework/hooks/git/pre-commit/orchestrator.sh +64 -64
- package/framework/hooks/git/pre-commit/specs.sh +50 -50
- package/framework/hooks/git/pre-push/run-tests.sh +44 -44
- package/framework/hooks/shared/build-check.js +1 -1
- package/framework/hooks/shared/comment-scanner.js +1 -1
- package/framework/hooks/shared/phase-utils.js +0 -5
- package/framework/hooks/shared/stdin-reader.js +26 -26
- package/framework/phases.json +4 -6
- package/framework/rubrics/backend.json +1 -1
- package/framework/rubrics/default.json +1 -1
- package/framework/rubrics/frontend.json +1 -1
- package/framework/rules/morph-workflow.md +6 -6
- package/framework/schemas/feature.schema.json +2 -28
- package/framework/schemas/tasks.schema.json +6 -2
- package/framework/skills/README.md +1 -2
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +145 -246
- package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
- package/framework/skills/core/morph-hotfix/SKILL.md +5 -3
- package/framework/skills/core/morph-implement/SKILL.md +101 -201
- package/framework/skills/core/morph-machine/SKILL.md +3 -3
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +171 -287
- package/framework/skills/core/morph-proposal/SKILL.md +4 -5
- package/framework/skills/core/morph-review/SKILL.md +109 -430
- package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +2 -2
- package/framework/skills/core/morph-uiux/SKILL.md +2 -3
- package/framework/standards/STANDARDS.json +10 -10
- package/framework/standards/ai-agents/agent-archetypes.md +1 -1
- package/framework/standards/ai-agents/agent-session.md +1 -1
- package/framework/standards/ai-agents/agent-spec.md +1 -1
- package/framework/standards/ai-agents/batching.md +1 -1
- package/framework/standards/ai-agents/context-providers.md +1 -1
- package/framework/standards/ai-agents/conversational-agent-with-phases.md +1 -1
- package/framework/standards/ai-agents/cost-and-budget.md +1 -1
- package/framework/standards/ai-agents/durable-workflows-hitl.md +1 -1
- package/framework/standards/ai-agents/guardrails.md +1 -1
- package/framework/standards/ai-agents/llm-runtime-defaults.md +1 -1
- package/framework/standards/ai-agents/mcp-server.md +1 -1
- package/framework/standards/ai-agents/mcp-tools.md +1 -1
- package/framework/standards/ai-agents/media-pipeline.md +1 -1
- package/framework/standards/ai-agents/media-video.md +1 -1
- package/framework/standards/ai-agents/middleware-patterns.md +1 -1
- package/framework/standards/ai-agents/modalities-image-gen.md +1 -1
- package/framework/standards/ai-agents/modalities-speech-to-text.md +1 -1
- package/framework/standards/ai-agents/modalities-text-to-speech.md +1 -1
- package/framework/standards/ai-agents/modalities-vision.md +1 -1
- package/framework/standards/ai-agents/multi-agent-patterns.md +1 -1
- package/framework/standards/ai-agents/observability-patterns.md +3 -3
- package/framework/standards/ai-agents/production.md +2 -2
- package/framework/standards/ai-agents/prompt-sources.md +1 -1
- package/framework/standards/ai-agents/providers/model-registry.md +1 -1
- package/framework/standards/ai-agents/rag-custom-pgvector.md +1 -1
- package/framework/standards/ai-agents/service-tier-flex.md +1 -1
- package/framework/standards/ai-agents/setup.md +1 -1
- package/framework/standards/ai-agents/structured-output.md +1 -1
- package/framework/standards/ai-agents/sweet-spot.md +1 -1
- package/framework/standards/ai-agents/testing-ai.md +1 -1
- package/framework/standards/ai-agents/tool-adapters.md +1 -1
- package/framework/standards/ai-agents/vector-data-extensions.md +1 -1
- package/framework/standards/ai-agents/workflows.md +1 -1
- package/framework/templates/CLAUDE.md.core.template +1 -1
- package/framework/templates/IDE-SNIPPETS.md +262 -262
- package/framework/templates/README.md +1 -1
- package/framework/templates/REGISTRY.json +0 -81
- package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
- package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -21
- package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
- package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
- package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
- package/framework/templates/docs/spec.md +19 -205
- package/framework/templates/docs/ui-components.md +52 -52
- package/framework/templates/docs/ui-design-system.md +76 -76
- package/framework/templates/docs/ui-flows.md +167 -167
- package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
- package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
- package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
- package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
- package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
- package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
- package/framework/templates/infrastructure/github/README.md +56 -56
- package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
- package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
- package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
- package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
- package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
- package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -95
- package/framework/templates/state.template.json +17 -17
- package/framework/templates/ui/design-system.css +226 -226
- package/package.json +2 -2
- package/src/commands/e2e/e2e.js +1 -1
- package/src/commands/project/heavy.js +1 -1
- package/src/commands/project/status.js +3 -3
- package/src/commands/state/approve.js +7 -49
- package/src/commands/state/gate-check.js +16 -106
- package/src/commands/state/gate-decision.js +1 -1
- package/src/commands/state/report-check.js +42 -17
- package/src/commands/validation/mutate.js +1 -1
- package/src/commands/validation/scout-check.js +28 -0
- package/src/commands/validation/validate-feature.js +5 -32
- package/src/commands/validation/verify.js +3 -4
- package/src/core/paths/output-schema.js +0 -2
- package/src/core/state/state-manager.js +1 -2
- package/src/core/templates/template-renderer.js +477 -477
- package/src/dashboard/app/lib/format.js +5 -0
- package/src/dashboard/app/main.jsx +13 -13
- package/src/dashboard/app/pages/FeatureDetail.jsx +16 -22
- package/src/dashboard/app/pages/Features.jsx +7 -7
- package/src/dashboard/app/pages/PhasePipeline.jsx +1 -1
- package/src/dashboard/app/pages/Rubrics.jsx +7 -11
- package/src/dashboard/scanner/docs-scanner.js +1 -2
- package/src/dashboard/scanner/features-scanner.js +3 -4
- package/src/dashboard/scanner/rubrics-scanner.js +12 -12
- package/src/dashboard/vite.config.js +24 -24
- package/src/lib/agents/output-collector.js +0 -1
- package/src/lib/detectors/design-system-detector.js +188 -188
- package/src/lib/evaluator-report.js +52 -334
- package/src/lib/generators/design-system-generator.js +508 -508
- package/src/lib/generators/handoff-generator.js +4 -5
- package/src/lib/installers/memory-installer.js +2 -2
- package/src/lib/mutation/manifest.js +8 -2
- package/src/lib/mutation/report.js +5 -3
- package/src/lib/mutation/runner.js +9 -7
- package/src/lib/risk-detector.js +60 -122
- package/src/lib/scout/scout-check.js +293 -0
- package/src/lib/tasks/task-classifier.js +30 -30
- package/src/lib/tasks/task-parser.js +209 -209
- package/src/lib/tasks/tasks-json-parser.js +7 -5
- package/src/lib/tasks/test-nodes.js +1 -1
- package/src/lib/tasks/test-runner.js +1 -1
- package/src/lib/validators/css/css-validator.js +340 -340
- package/src/lib/validators/reuse/reuse-validator.js +6 -8
- package/src/lib/validators/verify-runner.js +1 -1
- package/src/scripts/setup-infra.js +1 -1
- package/src/utils/agent-id-mapper.js +11 -11
- package/src/utils/agents-installer.js +5 -9
- package/src/utils/claude-settings-manager.js +300 -300
- package/src/utils/hooks-installer.js +3 -10
- package/src/utils/skills-installer.js +1 -1
- package/framework/evals/proof-rubrics/README.md +0 -76
- package/framework/evals/proof-rubrics/eval-de-modelo.md +0 -80
- package/framework/evals/proof-rubrics/fiacao.md +0 -54
- package/framework/evals/proof-rubrics/guard-cego.md +0 -52
- package/framework/evals/proof-rubrics/media-claims.md +0 -109
- package/framework/evals/proof-rubrics/mutation-proof.md +0 -107
- package/framework/evals/proof-rubrics/observabilidade-genai.md +0 -119
- package/framework/evals/proof-rubrics/verde-nao-e-coberto.md +0 -48
- package/framework/evals/rubrics/README.md +0 -45
- package/framework/evals/rubrics/architecture.md +0 -103
- package/framework/evals/rubrics/code-quality.md +0 -98
- package/framework/evals/rubrics/condensed.md +0 -57
- package/framework/evals/rubrics/contracts.md +0 -67
- package/framework/evals/rubrics/test-coverage.md +0 -99
- package/framework/evals/scenarios/dotnet-crud.yaml +0 -42
- package/framework/evals/scenarios/nextjs-auth.yaml +0 -38
- package/framework/evals/suites/standard.yaml +0 -5
- package/framework/hooks/claude-code/core/post-edit-typecheck.js +0 -127
- package/framework/skills/core/morph-eval/SKILL.md +0 -64
- package/framework/templates/.idea/morph-templates.xml +0 -92
- package/framework/templates/.vscode/morph-templates.code-snippets +0 -186
- package/framework/templates/docs/clarifications.md +0 -253
- package/framework/templates/docs/onboarding.md +0 -122
- package/framework/templates/docs/schema-analysis.md +0 -119
- package/framework/templates/docs/user-stories.md +0 -34
- 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
|
-
```
|