Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 44 additions & 10 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ exercises the real GNU `timeout` instead of the perl backend:
```bash
docker run --rm -v "$PWD":/repo:ro node:22-bookworm bash -c \
'apt-get update -qq && apt-get install -y -qq procps && cd /repo && bash scripts/test.sh'
# validated 2026-08: 57/57 cases, 5/5 properties, 49/49 invariants
# validated 2026-08: 86/86 cases, 5/5 properties, 77/77 invariants
```

The .NET heuristic was validated against the real SDK
Expand Down Expand Up @@ -155,15 +155,42 @@ into one of three levels:

| Level | Condition | What it does |
|---|---|---|
| GREEN | typecheck and tests pass | runs the full phases without asking |
| GREEN | typecheck and tests pass | runs the phases without asking; phase 2 stops at the checkpoint |
| YELLOW | partial net, or no test file in the stack | only deps and orphan files, no touching exports |
| RED | no tests and no typecheck | diagnoses only; nothing is deleted |
| RED | no tests and no typecheck, or a baseline already failing | diagnoses only; nothing is deleted |

A project that arrives with a red suite falls into RED, not YELLOW: with a
broken baseline there is no telling what the cleanup broke from what was
already broken, and since every commit demands a green gate, none of them would
happen. The skill names the failing check and stops there.

A stack with no test file at all does not count as tested: the gate does not
run the empty suite and the level stays at YELLOW. That covers Go and .NET
with no test file, a Rust crate with no `tests/*.rs` and no `#[test]`, and a
pytest run that exits 5 having collected nothing. If your suite lives outside
the usual place, promoting it is your call — the gate never promotes itself.
count an empty suite, whether it declined to run it or ran it and got nothing
back, and the level stays at YELLOW. That covers Go and .NET
with no test file, a Rust crate with no `tests/*.rs` and no `#[test]`, a Maven
or Gradle build with no `src/test` anywhere, and a pytest run that exits 5
having collected nothing. A manifest carried for tooling and nothing else — a
`requirements.txt` for the docs build, a `Gemfile` for fastlane — is not a stack
without a suite: with no source of that language in the repo, the gate says
nothing about it. If your suite lives outside the usual place, promoting it is
your call — the gate never promotes itself.

In JS/TS the same cap covers a sliced suite: with no `test` script and both
`test:unit` and `test:e2e` in the manifest, no slice answers for the whole
suite and the gate counts none of them. Promoting by hand is the wrong move
here, because the suite is not somewhere else, it is split; run every slice. A
lone slice does count as the suite, with one exception: watch mode never exits,
so the gate skips it. `watch`, `ui` and `debug` are read as whole segments of
the name, which catches `test:watch:all` and leaves `test:watchdog` alone. Being
skipped and being uncounted are not the same, though: `watch` and `debug` are a
mode of the suite, so `test:watch` next to a lone `test:unit` leaves that slice
as the suite; `test:ui` may well be a suite of its own, so it is skipped and
still counted, and `test:unit` next to it is a split.

In a polyglot repo the cap survives the other stacks. Go with tests next to a
JS half that was never counted still prints `checks=typecheck,test`, because
each word came from a different manifest — and there the gate refuses to say
GREEN, naming the stack that has no suite instead.

With the level announced, it creates the cleanup branch and proceeds:

Expand Down Expand Up @@ -218,9 +245,10 @@ rollback discards is what the skill itself created.
- A dynamic import with a string assembled at runtime is invisible to the
graph. The skill handles this by teaching knip (explicit entry) instead of
deleting, but it is worth reviewing the generated `knip.json`.
- RED level returns a report, not a cleanup. If the project has neither tests
nor typecheck, the first step is to create a minimal verification; the skill
points the way in the report itself.
- RED level returns a report, not a cleanup. That covers a project with
neither tests nor typecheck, and one that arrives with a red suite. With no
tests, the first step is to create a minimal verification; with a broken
suite, it is to fix the check the report names.
- Exit 124 is reserved for the watchdog, exactly as in GNU `timeout`: a
check that legitimately exits 124 under an active watchdog reads as TIMEOUT.
- With a single `.sln`/`.slnx` at the root the gate passes it explicitly to
Expand All @@ -233,6 +261,12 @@ rollback discards is what the skill itself created.
- A folder with no git falls into RED as well, even with typecheck and tests
passing. With no commit there is no rollback, and the rollback is what holds
up the autonomy of the rest of the pipeline.
- The unused-deps category runs the package manager's plain install after
pruning the manifest, so the lockfile is rewritten and `node_modules`
re-resolved. That part is outside the rollback: `git restore` brings back
`package.json` and the lockfile, never the installed tree. Run the install
again if that category is the one that fails; the other two never touch the
manifest and do not need it.

## Credits

Expand Down
57 changes: 45 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ exercita o GNU `timeout` real em vez do backend perl:
```bash
docker run --rm -v "$PWD":/repo:ro node:22-bookworm bash -c \
'apt-get update -qq && apt-get install -y -qq procps && cd /repo && bash scripts/test.sh'
# validado em 08/2026: 57/57 casos, 5/5 propriedades, 49/49 invariantes
# validado em 08/2026: 86/86 casos, 5/5 propriedades, 77/77 invariantes
```

A heurística .NET foi validada contra o SDK real (`mcr.microsoft.com/dotnet/sdk:8.0`
Expand Down Expand Up @@ -152,15 +152,41 @@ e se classifica em um de três níveis:

| Nível | Condição | O que ela faz |
|---|---|---|
| GREEN | typecheck e testes passam | executa as fases inteiras sem perguntar |
| GREEN | typecheck e testes passam | executa as fases sem perguntar; a fase 2 para no checkpoint |
| YELLOW | rede parcial, ou nenhum arquivo de teste no stack | só deps e arquivos órfãos, sem mexer em exports |
| RED | sem testes e sem typecheck | só diagnostica; nada é deletado |

Stack sem nenhum arquivo de teste não conta como testado: o gate não roda a
suíte vazia e o nível fica em YELLOW. Vale para Go e .NET sem arquivo de
teste, para crate Rust sem `tests/*.rs` nem `#[test]`, e para pytest que sai
5 sem coletar nada. Se a sua suíte mora fora do lugar padrão, a promoção é
sua — o gate não se promove sozinho.
| RED | sem testes e sem typecheck, ou baseline já vermelho | só diagnostica; nada é deletado |

Projeto que já chega com a suíte vermelha cai em RED, não em YELLOW: com o
baseline quebrado não dá para separar o que a limpeza quebrou do que já estava
quebrado, e como todo commit exige gate verde, nenhum deles aconteceria. A
skill diz qual check falhou e para por aí.

Stack sem nenhum arquivo de teste não conta como testado: o gate não conta
suíte vazia, seja porque não a rodou, seja porque rodou e não voltou nada, e o
nível fica em YELLOW. Vale para Go e .NET sem arquivo de
teste, para crate Rust sem `tests/*.rs` nem `#[test]`, para build Maven ou
Gradle sem nenhum `src/test`, e para pytest que sai 5 sem coletar nada.
Manifesto que está ali só por ferramenta — um `requirements.txt` do build da
documentação, um `Gemfile` do fastlane — não é stack sem suíte: sem código
daquela linguagem no repositório, o gate não fala dele. Se a sua suíte mora
fora do lugar padrão, a promoção é sua — o gate não se promove sozinho.

Em JS/TS o mesmo cap pega a suíte fatiada: sem script `test`, com `test:unit`
e `test:e2e` no manifesto, nenhuma fatia responde pela suíte inteira e o gate
não conta nenhuma delas. Promover à mão aqui é o caminho errado, porque a
suíte não está fora do lugar, está dividida; rode as fatias todas. Uma fatia
sozinha vale como a suíte, com uma exceção: modo watch nunca termina, então o
gate não o executa. `watch`, `ui` e `debug` são lidos como segmentos inteiros
do nome, o que pega `test:watch:all` e deixa `test:watchdog` em paz. Só que não
executar e não contar são coisas diferentes: `watch` e `debug` são um modo da
suíte, então `test:watch` ao lado de um `test:unit` sozinho deixa essa fatia
valendo como a suíte; `test:ui` pode muito bem ser uma suíte à parte, então ele
não roda e mesmo assim conta, e aí `test:unit` ao lado dele já é uma divisão.

Em repositório poliglota o cap sobrevive aos outros stacks. Go com testes ao
lado de uma metade JS que ninguém contou ainda imprime `checks=typecheck,test`,
porque cada palavra veio de um manifesto diferente — e aí o gate se recusa a
dizer GREEN, e nomeia o stack que ficou sem suíte.

Com o nível anunciado, ela cria a branch de limpeza e segue:

Expand Down Expand Up @@ -213,9 +239,10 @@ rollback joga fora foi ela mesma que criou.
- Import dinâmico com string montada em runtime é invisível ao grafo. A skill
trata isso ensinando o knip (entry explícito) em vez de deletar, mas vale
revisar o `knip.json` gerado.
- Nível RED devolve relatório, não limpeza. Se o projeto não tem teste nem
typecheck, o primeiro passo é criar uma verificação mínima; a skill aponta o
caminho no próprio relatório.
- Nível RED devolve relatório, não limpeza. Cai aí quem não tem teste nem
typecheck, e também quem chega com a suíte vermelha. Sem teste, o primeiro
passo é criar uma verificação mínima; com a suíte quebrada, é consertar o
check que o relatório nomeia.
- Exit 124 é reservado ao watchdog, igual ao GNU `timeout`: um check que
legitimamente sai 124 sob watchdog ativo é lido como TIMEOUT.
- Com uma única `.sln`/`.slnx` na raiz o gate a passa explícita ao `dotnet`;
Expand All @@ -228,6 +255,12 @@ rollback joga fora foi ela mesma que criou.
- Pasta sem git também cai em RED, mesmo com typecheck e testes passando. Sem
commit não existe rollback, e é o rollback que sustenta a autonomia do resto
do pipeline.
- A categoria de deps roda o install simples do gerenciador depois de podar o
manifesto, então o lockfile é reescrito e o `node_modules` é resolvido de
novo. Essa parte fica fora do rollback: o `git restore` traz de volta o
`package.json` e o lockfile, nunca a árvore instalada. Se for essa categoria
que falhar, rode o install outra vez; as outras duas não mexem no manifesto e
não precisam.

## Créditos

Expand Down
Loading
Loading