@orkastery/cli 0.6.2 → 0.6.4
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/adapters/claude-code/README.md +1 -1
- package/adapters/hermes/README.md +1 -1
- package/adapters/openclaw/openclaw.plugin.json +1 -1
- package/adapters/openclaw/package.json +1 -1
- package/assets/adapters-en/claude-code/README.md +1 -1
- package/assets/adapters-en/hermes/README.md +1 -1
- package/assets/docs/padroes/roadmap-de-produto.md +1 -1
- package/assets/docs-en/padroes/roadmap-de-produto.md +1 -1
- package/assets/references-en/testing-patterns.md +1 -1
- package/dist/agendador-cpu.js +49 -5
- package/dist/catalogo.js +3 -3
- package/dist/{canarios-hitl.js → cenarios-hitl.js} +7 -7
- package/dist/{canarios-i43.js → cenarios-i43.js} +16 -16
- package/dist/{canarios-pulse.js → cenarios-pulse.js} +3 -3
- package/dist/{canarios-rm057.js → cenarios-rm057.js} +3 -3
- package/dist/{canarios-sensores.js → cenarios-sensores.js} +9 -8
- package/dist/{canarios.js → cenarios.js} +22 -22
- package/dist/check-agil.js +2 -1
- package/dist/check-paralelo.js +337 -14
- package/dist/claims.js +1 -1
- package/dist/docs.js +1 -1
- package/dist/evalrunner.js +37 -37
- package/dist/hitl-canais.js +1 -1
- package/dist/hitl-presentation.js +1 -1
- package/dist/hitl-tempo-parado.js +1 -1
- package/dist/index.js +4 -4
- package/dist/init.js +1 -1
- package/dist/locale-ajuda.js +6 -6
- package/dist/locale-operacao-restante.js +18 -18
- package/dist/sandbox.js +11 -11
- package/dist/security-scan/agil.js +28 -12
- package/dist/security-scan/executor.js +1 -0
- package/dist/security-scan/incremental.js +289 -0
- package/dist/security-scan/trabalhador.js +53 -5
- package/dist/verify.js +12 -1
- package/eval/README.md +8 -8
- package/eval/fixtures/fx-codex-dry/caso.json +1 -1
- package/eval/fixtures/fx-happy/caso.json +1 -1
- package/eval/fixtures/fx-indice-reversao/caso.json +1 -1
- package/eval/fixtures/fx-listagem-abertas/caso.json +1 -1
- package/eval/fixtures/fx-omnicanal/caso.json +1 -1
- package/eval/fixtures/fx-pedido-colado/caso.json +1 -1
- package/eval/fixtures/fx-schema-drift/caso.json +1 -1
- package/package.json +1 -1
- package/references/testing-patterns.md +1 -1
|
@@ -41,9 +41,11 @@ exports.comCacheNoHost = comCacheNoHost;
|
|
|
41
41
|
* `runScanAgil`, um trabalhador por ferramenta, ao mesmo tempo.
|
|
42
42
|
*/
|
|
43
43
|
const fs = __importStar(require("node:fs"));
|
|
44
|
+
const path = __importStar(require("node:path"));
|
|
44
45
|
const executor_1 = require("./executor");
|
|
45
46
|
const guard_1 = require("./guard");
|
|
46
47
|
const run_1 = require("./run");
|
|
48
|
+
const incremental_1 = require("./incremental");
|
|
47
49
|
/**
|
|
48
50
|
* S6 do CHECK: o binario nativo usa o cache por usuario que o Docker monta (o caminho do host no lugar do caminho do
|
|
49
51
|
* container), e nao o `/tmp` compartilhado do catalogo, onde outro usuario da maquina deixaria um banco do Trivy.
|
|
@@ -51,10 +53,49 @@ const run_1 = require("./run");
|
|
|
51
53
|
function comCacheNoHost(t) {
|
|
52
54
|
if (!t.cacheMounts?.length)
|
|
53
55
|
return t;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
const trocar = (args) => {
|
|
57
|
+
let resposta = args;
|
|
58
|
+
for (const m of t.cacheMounts ?? [])
|
|
59
|
+
resposta = resposta.map((a) => a.split(m.container).join(m.host));
|
|
60
|
+
return resposta;
|
|
61
|
+
};
|
|
62
|
+
return { ...t, args: trocar(t.args), ...(t.argsByStack
|
|
63
|
+
? { argsByStack: Object.fromEntries(Object.entries(t.argsByStack).map(([stack, args]) => [stack, trocar(args ?? [])])) }
|
|
64
|
+
: {}) };
|
|
65
|
+
}
|
|
66
|
+
/** Semgrep é a garantia por arquivo: roda apenas conteúdo mudado e conserva os achados dos demais arquivos. */
|
|
67
|
+
function semgrepIncremental(trabalho, ferramenta, consulta, executar) {
|
|
68
|
+
if (ferramenta.name !== 'semgrep' || !consulta.anterior || consulta.escopo !== 'arquivo' || !ferramenta.argsByStack)
|
|
69
|
+
return null;
|
|
70
|
+
if (new Set(trabalho.units.map((u) => u.stack)).size !== trabalho.units.length)
|
|
71
|
+
return null;
|
|
72
|
+
const existentes = consulta.alterados.filter((arquivo) => {
|
|
73
|
+
try {
|
|
74
|
+
return fs.statSync(path.join(trabalho.ctx.headTree, arquivo)).isFile();
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
const argsByStack = {};
|
|
81
|
+
const unidades = trabalho.units.filter((unidade) => {
|
|
82
|
+
const prefixo = unidade.dir === '.' ? '' : `${unidade.dir.replace(/\\/g, '/')}/`;
|
|
83
|
+
const alvos = existentes.filter((arquivo) => !prefixo || arquivo.startsWith(prefixo));
|
|
84
|
+
const base = unidade.stack ? ferramenta.argsByStack?.[unidade.stack] : undefined;
|
|
85
|
+
if (!base?.length || alvos.length === 0 || !unidade.stack)
|
|
86
|
+
return false;
|
|
87
|
+
argsByStack[unidade.stack] = [...base.slice(0, -1), ...alvos.map((arquivo) => `{src}/${arquivo}`)];
|
|
88
|
+
return true;
|
|
89
|
+
});
|
|
90
|
+
const mudaram = new Set(consulta.alterados);
|
|
91
|
+
const preservados = consulta.anterior.head.filter((achado) => !mudaram.has(achado.file));
|
|
92
|
+
if (unidades.length === 0)
|
|
93
|
+
return { ...consulta.anterior, head: preservados };
|
|
94
|
+
const parcial = executar({ ...ferramenta, argsByStack }, unidades, true);
|
|
95
|
+
if (parcial.run.state !== 'ran')
|
|
96
|
+
return parcial;
|
|
97
|
+
const novos = parcial.head.filter((achado) => mudaram.has(achado.file));
|
|
98
|
+
return { ...parcial, head: [...preservados, ...novos], base: consulta.anterior.base };
|
|
58
99
|
}
|
|
59
100
|
if (require.main === module) {
|
|
60
101
|
const trabalho = JSON.parse(fs.readFileSync(process.argv[2], 'utf8'));
|
|
@@ -65,7 +106,14 @@ if (require.main === module) {
|
|
|
65
106
|
// chooseExecutor escolher o nativo); sem ele, o Docker, como no Classic.
|
|
66
107
|
const sonda = (0, executor_1.defaultProbe)();
|
|
67
108
|
const probe = trabalho.nativoPrimeiro ? { ...sonda, dockerCli: () => false, dockerDaemon: () => false } : sonda;
|
|
68
|
-
const
|
|
109
|
+
const ferramenta = trabalho.nativoPrimeiro ? comCacheNoHost(trabalho.tool) : trabalho.tool;
|
|
110
|
+
const executar = (tool, units, somenteHead = false) => (0, run_1.scanTool)(tool, units, trabalho.config, { ...resto, ...(somenteHead ? { base: resto.head, baseTree: resto.headTree, rangeCommits: 0 } : {}),
|
|
111
|
+
deadline: deadline ?? Infinity, probe, guard, images: new Map() });
|
|
112
|
+
const consulta = trabalho.incremental ? (0, incremental_1.consultarCacheIncremental)(trabalho, ferramenta) : null;
|
|
113
|
+
const r = consulta?.reutilizado ?? (consulta ? semgrepIncremental(trabalho, ferramenta, consulta, executar) : null)
|
|
114
|
+
?? executar(ferramenta, trabalho.units);
|
|
115
|
+
if (trabalho.incremental && !consulta?.reutilizado)
|
|
116
|
+
(0, incremental_1.gravarCacheIncremental)(trabalho, ferramenta, r);
|
|
69
117
|
process.stdout.write(JSON.stringify(r));
|
|
70
118
|
}
|
|
71
119
|
finally {
|
package/dist/verify.js
CHANGED
|
@@ -448,10 +448,21 @@ function executorDoGrupo(manifesto, ambienteDaRodada, instabilidade, leitor, con
|
|
|
448
448
|
const processos = (0, check_paralelo_1.rodarEmParalelo)(paraOAuxiliar, ambienteDoVerify(), medicaoCpu ? { medicaoCpu } : {});
|
|
449
449
|
if (processos.agendamentoCpu) {
|
|
450
450
|
const p = processos.agendamentoCpu;
|
|
451
|
+
const medida = p.medicaoCpuIntegracao;
|
|
452
|
+
const evidencia = medida
|
|
453
|
+
? `; CPU da integracao: arvore ${Math.round(medida.arvoreMs)} ms + conteineres ${Math.round(medida.containersMs)} ms` +
|
|
454
|
+
` = ${Math.round(medida.totalMs)} ms, pico ${medida.picoCpus ?? 'indisponivel'} CPU, ` +
|
|
455
|
+
(medida.completa ? 'medida completa' : medida.anomalia
|
|
456
|
+
? `medida anomala (${medida.anomalia})`
|
|
457
|
+
: `medida incompleta (${medida.motivosDosAusentes.map((x) => `${x.container}: ${x.motivo}`).join('; ')})`)
|
|
458
|
+
: '';
|
|
451
459
|
executar.pecas = [{ peca: 'agendador-cpu', estado: p.saturado ? 'fallback' : 'agile',
|
|
452
460
|
motivo: p.saturado
|
|
453
461
|
? `${p.motivo}; hermetica com ${p.cpusHermetica}/${p.cpus} CPUs, varredura e integracao depois dela`
|
|
454
|
-
: `${p.cpusHermetica}/${p.cpus} CPUs na hermetica
|
|
462
|
+
: `${p.cpusHermetica}/${p.cpus} CPUs na hermetica; depois da varredura ${p.cpusHermeticaAposVarredura}` +
|
|
463
|
+
` e depois da integracao ${p.cpusHermeticaAposReserva}; reserva da integracao ${p.fonteCpusIntegracao}` +
|
|
464
|
+
` (${p.cpusIntegracao} CPU), ${p.integracaoEmParalelo ? 'em paralelo com a varredura' : 'encadeada depois da varredura'}` +
|
|
465
|
+
`; conteineres com ${p.cpusPorContainer} CPU${evidencia}`,
|
|
455
466
|
...(p.saturado ? { fallback: 'ordem do Classic para hermetica, varredura e integracao' } : {}) }];
|
|
456
467
|
}
|
|
457
468
|
const stealPct = janela.fechar();
|
package/eval/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# eval/
|
|
2
2
|
|
|
3
3
|
Avaliacao deterministica do Orkastery, rodada por `ork eval`. Regra do catalogo original,
|
|
4
|
-
mantida e agora executavel: **skill sem eval nao entra**, e comportamento sem
|
|
4
|
+
mantida e agora executavel: **skill sem eval nao entra**, e comportamento sem cenario tambem nao.
|
|
5
5
|
|
|
6
6
|
```bash
|
|
7
|
-
ork eval #
|
|
8
|
-
ork eval --so-
|
|
7
|
+
ork eval # cenarios + corpus das skills
|
|
8
|
+
ork eval --so-cenarios # so o comportamento do nucleo
|
|
9
9
|
ork eval --skill code-reviewer # so uma skill
|
|
10
|
-
ork eval --
|
|
10
|
+
ork eval --cenario fx-stale-base # so um cenario
|
|
11
11
|
ork eval --json # para o CI
|
|
12
12
|
```
|
|
13
13
|
|
|
@@ -35,14 +35,14 @@ monta uma copia do catalogo, apaga uma regra, planta uma skill sem eval e ancora
|
|
|
35
35
|
dentro da tabela de desculpas, e confere que os tres ficam vermelhos. Corpus que fica verde
|
|
36
36
|
depois de a regra sumir nao protege nada.
|
|
37
37
|
|
|
38
|
-
## `fixtures/`, os
|
|
38
|
+
## `fixtures/`, os cenarios
|
|
39
39
|
|
|
40
40
|
Seis comportamentos do nucleo, exercitados ponta a ponta contra git de verdade, em repositorio
|
|
41
41
|
temporario, sem rede e sem credencial. A expectativa de cada um e **dado versionado**
|
|
42
|
-
(`caso.json`), e o executor e codigo tipado (`core/src/
|
|
42
|
+
(`caso.json`), e o executor e codigo tipado (`core/src/cenarios.ts`): assim, mudar o que se
|
|
43
43
|
espera do produto e um diff que aparece na revisao, revisavel por quem nao le TypeScript.
|
|
44
44
|
|
|
45
|
-
|
|
|
45
|
+
| Cenario | O que ele impede de voltar |
|
|
46
46
|
|---|---|
|
|
47
47
|
| `fx-happy` | O caminho feliz parar de funcionar sem ninguem notar |
|
|
48
48
|
| `fx-hallucination` | Citacao de arquivo e de teste que nao existem passar no gate |
|
|
@@ -54,7 +54,7 @@ espera do produto e um diff que aparece na revisao, revisavel por quem nao le Ty
|
|
|
54
54
|
Ha ainda duas fixtures de contrato, mais antigas, rodadas pela suite (`npm test`) em
|
|
55
55
|
`core/test/eval.test.ts`: `b0-slug-e-modos` (a #TAG do pedido vira slug, blocos e pausas) e
|
|
56
56
|
`b2-master-log` (o contrato congelado do MASTER log, com as mutacoes que precisam reprovar).
|
|
57
|
-
A segunda tem dois consumidores de proposito: a suite e o
|
|
57
|
+
A segunda tem dois consumidores de proposito: a suite e o cenario `fx-schema-drift`.
|
|
58
58
|
|
|
59
59
|
## `casos/`, o corpus das skills
|
|
60
60
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "fx-codex-dry",
|
|
3
3
|
"sobre": "O setup do bloco leva o despacho governado ao controller Codex app-server via stdio em dry-run; o default continua claude-bg.",
|
|
4
|
-
"porque": "Novos despachos governados precisam de entrada bidirecional e controller próprio. O
|
|
4
|
+
"porque": "Novos despachos governados precisam de entrada bidirecional e controller próprio. O cenário exige o comando app-server via stdio, preserva runtime/modelo/esforço e garante que dry-run não despacha. Provas de recepção nativa são separadas.",
|
|
5
5
|
"precisa": [
|
|
6
6
|
"git"
|
|
7
7
|
],
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "fx-happy",
|
|
3
3
|
"sobre": "O ciclo que funciona: thread aberta no modo do pedido, fase montada com prompt gravado e hash, e claim que passa na reexecucao no HEAD real.",
|
|
4
|
-
"porque": "Um corpus que so tem
|
|
4
|
+
"porque": "Um corpus que so tem cenario de falha nao percebe quando o caminho feliz para de funcionar. Este e o unico que reprova se o produto deixar de fazer o basico.",
|
|
5
5
|
"precisa": [
|
|
6
6
|
"git"
|
|
7
7
|
],
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "fx-indice-reversao",
|
|
3
3
|
"sobre": "Uma entrega revertida depois do ship se rebaixa SOZINHA no caminho que o produto percorre de verdade: `ork master` mostra a reversão em vez de \"sem tropeço\", e a aceitação por omissão grava score 2, não 5, sem ninguém pontuar nada.",
|
|
4
|
-
"porque": "É o
|
|
4
|
+
"porque": "É o cenário que libera a saída da fila de ratificação de score, e prova a propriedade exata que a fila não entregava: ela tinha 13 entradas esperando nota humana justamente porque lembrete não converte em nota, e nenhuma delas dizia se a entrega havia sido desfeita depois. Na primeira versão ele chamava `registrarReversaoDaEntrega` À MÃO, e o CHECK de 20/09/2026 provou com `git revert` real que isso não provava nada: no produto o único chamador era o começo de um `ork ship` SEGUINTE da mesma thread, que depois do MASTER não acontece, e `ork master --aceitar-omissao` gravava score 5 com a justificativa \"sem reversão\" sobre uma entrega desfeita. Agora o cenário roda o que o dono roda: `tabelaDeEntregas` e `aceitarPorOmissao`. A detecção ancora no marcador que o próprio git escreve (\"This reverts commit <sha>\") e não em diff de árvore, porque um arquivo que voltou ao estado anterior pode ser trabalho de outra thread, e chamar isso de reversão rebaixaria entrega alheia por coincidência. A idempotência é condição dura agora que a detecção roda em toda consulta: sem ela, abrir a tabela afundaria a nota da thread a cada vez.",
|
|
5
5
|
"precisa": [
|
|
6
6
|
"git"
|
|
7
7
|
],
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "fx-listagem-abertas",
|
|
3
3
|
"sobre": "O default de `ork thread list` mostra o que ainda pede atenção (aberta e pausada) e omite a fechada, avisando que omitiu; `--todas` abre o resto.",
|
|
4
|
-
"porque": "O custo medido era o DEFAULT e não a falta de flag: em 20/09/2026 o comando imprimia 137 linhas para as 31 que interessavam, e 71 delas (52 por cento) eram threads `Sessão adotada claude-bg <uuid>`, lixo do mecanismo de adoção de 07 a 14/09. Quem pagava era quem não sabia que existia flag, porque flag nenhuma existia. A thread PAUSADA é a razão de o default ser \"não fechadas\" e não \"só abertas\", que era o texto do GOAL: esconder a pausada seria esconder justamente a que mais pede atenção. O
|
|
4
|
+
"porque": "O custo medido era o DEFAULT e não a falta de flag: em 20/09/2026 o comando imprimia 137 linhas para as 31 que interessavam, e 71 delas (52 por cento) eram threads `Sessão adotada claude-bg <uuid>`, lixo do mecanismo de adoção de 07 a 14/09. Quem pagava era quem não sabia que existia flag, porque flag nenhuma existia. A thread PAUSADA é a razão de o default ser \"não fechadas\" e não \"só abertas\", que era o texto do GOAL: esconder a pausada seria esconder justamente a que mais pede atenção. O cenário roda em sandbox porque `ork thread list` dentro de uma worktree lê o estado da árvore principal (M8): conferir \"no projeto\" seria afirmar contagem de artefato vivo, que reprova sozinha quando a branch cresce.",
|
|
5
5
|
"precisa": ["git"],
|
|
6
6
|
"esperado": {
|
|
7
7
|
"noDefault": ["aberta", "pausada"],
|
|
@@ -13,5 +13,5 @@
|
|
|
13
13
|
"humanosSemIngresso": 0,
|
|
14
14
|
"ofertaDisponivel": 4
|
|
15
15
|
},
|
|
16
|
-
"porque": "D12 exige HITL omnicanal nos quatro hosts homologados com identidade, correlacao e recibo equivalentes, e FX1 exige que a chave de ingresso seja de cada canal. O
|
|
16
|
+
"porque": "D12 exige HITL omnicanal nos quatro hosts homologados com identidade, correlacao e recibo equivalentes, e FX1 exige que a chave de ingresso seja de cada canal. O cenario impede quatro regressoes: reduzir a lista de canais, voltar a gravar recibo que nao distingue Hermes de OpenClaw, aceitar resposta sem ingresso homologado (aprovacao cega, transcrito de tool ou canal de outro transporte), e voltar a compartilhar uma chave entre os dois canais de telegram."
|
|
17
17
|
}
|
|
@@ -16,5 +16,5 @@
|
|
|
16
16
|
"tempoParadoMs": 0,
|
|
17
17
|
"adaptadoresComARegra": 4
|
|
18
18
|
},
|
|
19
|
-
"porque": "Na noite de 01 para 02/10 o condutor parou mais de 10 h pedindo um confirmo em texto livre a um pedido colado que já trazia a autorização. O
|
|
19
|
+
"porque": "Na noite de 01 para 02/10 o condutor parou mais de 10 h pedindo um confirmo em texto livre a um pedido colado que já trazia a autorização. O cenário prova, contra o código real, que o #Auto não pausa, a dúvida vira decisão informada, o confirmo em texto é recusado sem gravar nada, ninguém aparece esperando no pulse, o tempo parado é zero e os quatro adaptadores dizem a regra."
|
|
20
20
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "fx-schema-drift",
|
|
3
3
|
"sobre": "Seis mutacoes do MASTER log valido: contrato trocado sem trocar a versao, score fora da escala, classe de falha inventada, fase fora do ciclo canonico, justificativa vazia e evidencia ausente.",
|
|
4
|
-
"porque": "O MASTER log e contrato publico congelado (ork.master-log/v1). Se ele aceitar mutacao em silencio, a telemetria de conducao deixa de ser comparavel entre threads e o score vira opiniao com formato de dado. O
|
|
4
|
+
"porque": "O MASTER log e contrato publico congelado (ork.master-log/v1). Se ele aceitar mutacao em silencio, a telemetria de conducao deixa de ser comparavel entre threads e o score vira opiniao com formato de dado. O cenario le a mesma fixture b2-master-log que a suite ja usa: uma fonte, dois consumidores.",
|
|
5
5
|
"precisa": [],
|
|
6
6
|
"esperado": {
|
|
7
7
|
"validoPassa": true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orkastery/cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.4",
|
|
4
4
|
"description": "A software factory of AI agents that proves its work: parallel threads, verified results, and short decisions only when they matter.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/orkastery/orkastery#readme",
|
|
@@ -33,7 +33,7 @@ flowchart LR
|
|
|
33
33
|
| 6 | A suite roda no HEAD real, no diretorio de trabalho da thread. Passagem relatada por agente nao e passagem. | `ork verify <thread>` reexecuta os comandos do manifesto e as claims, e carimba o commit real. |
|
|
34
34
|
| 7 | Nenhum teste foi desligado, marcado para pular ou afrouxado para fechar a entrega. Pular teste e mudanca de escopo com registro, nunca ajuste de rota silencioso. | Diff sobre os arquivos de teste, procurando teste removido, `skip` e assercao relaxada. |
|
|
35
35
|
| 8 | Nenhuma assercao foi trocada pelo valor que o codigo produz hoje sem que alguem tenha dito por que o valor certo mudou. Ajustar o esperado ao observado e a forma mais comum de transformar defeito em contrato. | Diff das assercoes contra a intencao declarada no PLAN. |
|
|
36
|
-
| 9 | Teste nao depende de rede, de credencial paga nem de relogio de parede. Fixture deterministica e o padrao; o que precisa de mundo externo fica fora do gate e e declarado. | Rodar a suite offline. No `ork`, os
|
|
36
|
+
| 9 | Teste nao depende de rede, de credencial paga nem de relogio de parede. Fixture deterministica e o padrao; o que precisa de mundo externo fica fora do gate e e declarado. | Rodar a suite offline. No `ork`, os cenarios de `ork eval` montam repositorio git temporario e nao tocam a rede. |
|
|
37
37
|
| 10 | Teste nao depende de outro teste nem da ordem de execucao. Estado compartilhado entre casos e defeito, mesmo quando a suite esta verde hoje. | Rodar em ordem embaralhada ou isoladamente o caso suspeito. |
|
|
38
38
|
|
|
39
39
|
## Contagem e cobertura
|