atomwrite 0.1.38

Atomic file operations CLI for LLM agents — read, write, edit, search, replace with NDJSON output
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
# Guia de Testes do atomwrite


[Read in English](TESTING.md)


## Objetivos
- Gastar menos tokens: um objeto NDJSON por chamada substitui o encadeamento de shell, e o `commands --brief` responde em 5.020 bytes onde a árvore completa custa 67.492
- Reduzir o custo financeiro, porque a conta acompanha os tokens e as idas e voltas, e uma chamada substitui as várias chamadas de shell que uma tarefa de arquivo pede
- Reduzir os erros com escrita atômica e checksum BLAKE3 em toda resposta, então um resultado velho ou parcial aparece em vez de ser presumido
- Reduzir as alucinações com discriminador `type`, schema fechado por verbo e código de saída que nomeia a falha
- Reduzir o tempo gasto em cada tarefa, porque a leitura, a busca e a escrita rodam num processo só em vez de numa corrente de ferramentas de shell
- Reduzir a latência com execução local e sem rede no caminho do arquivo, medida nas medianas de 5,6 ms no `read` e 6,7 ms no `write` de arquivo pequeno
- Multiplicar a velocidade das operações com `batch`, `edit-loop`, `replace`, `case`, `transform` e `codemod` resolvendo muitos arquivos numa chamada
- Reduzir o lead time entre a solicitação e o resultado verificado, porque a resposta já traz o checksum, o `target_resolved` e o próximo passo
- Tornar o agente mais rápido, mais veloz, mais eficaz e mais assertivo, já que o código de saída, o `code` e o `suggestion` dizem o que fazer sem tentar às cegas


## Linha de Release Atual: v0.1.38
- A suíte completa saiu 0 em 2026-11-04 com `cargo test --no-fail-fast`, e a corrida imprimiu 134 blocos `test result: ok`
- O schema do envelope de falha exige `ok`, `code` e `exit`, então uma suíte que afirmava `error: true` passa a ler `.ok == false` mais `.code`
- Nenhum teste foi acrescentado para as correções da 0.1.38 e as suítes existentes foram atualizadas no lugar, porque a prova é a execução real no binário instalado e não uma maquete nova
- O binário 0.1.38 foi executado em 2026-11-04 e respondeu ao `agent-surface` com 47 ferramentas, 42 códigos de saída e 34 números distintos, e ao `config list` com 43 chaves
- Os 47 exemplos do smoke test abaixo rodaram contra o binário 0.1.38 em 2026-11-04, e 46 saíram 0 enquanto o `watch` saiu 78


## O Que Há de Novo na v0.1.37
- Superfície completa da CLI sob política de teste: 47 subcomandos (`CANONICAL_SUBCOMMAND_COUNT`): `agent-surface`, `apply`, `backup`, `batch`, `calc`, `case`, `codemod`, `commands`, `completions`, `config`, `copy`, `count`, `del`, `delete`, `diff`, `doctor`, `edit`, `edit-loop`, `extract`, `extract-artifact`, `get`, `hash`, `list`, `locale`, `move`, `outline`, `prune-backups`, `query`, `read`, `recipe`, `regex`, `replace`, `rollback`, `scope`, `scratch`, `search`, `semantic-merge`, `semantic-search`, `set`, `sparse`, `stat`, `transform`, `verify`, `wal-heal`, `wal-stats`, `watch`, `write`
- Suites de documentação: `cargo test --test docs_coverage --test docs_consistency --test docs_exit_codes --test docs_schemas_sync`
- O `docs_coverage` exige todo subcomando e toda flag local em cada documento principal, e o `docs_consistency` confere todo total citado contra o binário real e prova que o próprio leitor reprova uma contagem velha
- O `docs_exit_codes` compara as tabelas de saída do README com o `agent-surface`, e o `docs_schemas_sync` mantém os arquivos de schema e os nomes publicados em passo
- Verbo `config`: `cargo test --test cli_v0137_config_verb` (ida e volta de set/get/unset, escopo obrigatório, valor inválido não grava nada, camadas chave a chave, gravação atômica 0600)
- Evento terminal e relato de falhas: `cargo test --test cli_v0137_terminal_event --test cli_v0137_terminal_pipe --test cli_v0137_terminal_schema` (um evento terminal, `MATCH_FAILED` para par ausente, `files_failed` e `errors[]`, exit 141 com leitor fechado, os seis schemas de resumo)
- Retry, cancelamento e limites: `cargo test --test cli_v0137_retry_deadline --test cli_v0137_retry_guards --test cli_v0137_cancel --test cli_v0137_search_rss --test cli_v0137_wal_startup_heal`
- Guardas de código-fonte (quebram o build): `cargo test --test cli_v0137_hardcode_guard --test cli_v0137_let_underscore_guard`
- `cli_v0137_hardcode_guard` varre o código de produção em `src/` (pula arquivos `constants*`, arquivos de teste, itens `#[cfg(test)]` e comentários). Ele proíbe literais de duração em `Duration::from_*`, o infixo `.bak.`, nomes de sidecar e de sonda e números de unidade de tempo fora de `constants*`, e proíbe `env::var`, `env::var_os`, `env::vars` e `Command::new` em qualquer código de produção. Também confere que o repositório não traz nenhum arquivo `.sh` nem diretório de automação remota. A lista `ALLOWED` de exceções está vazia
- `cli_v0137_let_underscore_guard` proíbe `let _ =` em código de produção, e proíbe ali `.unwrap()`, `.expect(`, `unreachable!(`, `panic!(`, `todo!(` e `unimplemented!(`. As duas listas de exceção estão vazias
- Harness hermético: todo teste que roda o binário passa por `common::hermetic(...)` em `tests/common/mod.rs`. O filho recebe `HOME`, `USERPROFILE`, `APPDATA`, `LOCALAPPDATA`, `XDG_CONFIG_HOME`, `XDG_DATA_HOME` e `XDG_STATE_HOME` vazios sob o diretório tmp do target do cargo, `LANG` e `LC_ALL` fixados em `en_US.UTF-8`, e `LANGUAGE` e `LC_MESSAGES` removidos
- O `XDG_CACHE_HOME` continua apontando para o cache real da máquina de propósito: a feature `ast` baixa os parsers tree-sitter ali no primeiro uso, e um cache vazio mandaria os testes de `outline`, `query` e `--syntax-check` para a rede
- Um teste que precisa da própria config redireciona `HOME` e `XDG_*` depois de chamar `hermetic`, porque a chamada `env` posterior vence
- Versão e build slim: `cargo test --test cli_v0137_version`; `cargo xtask check-features` confere os conjuntos de features `core` e `core,watch`
- A execução de teste suportada usa as features padrão: `cargo test --locked`. Não use `cargo test --no-default-features --features core` como gate (veja Perfis de teste local)
- DoD local (só gates locais, sem automação remota): `cargo test --lib --tests`, `cargo clippy --all-targets -- -D warnings`
- Pin docs/agentes em `^0.1.38`


## Smoke Test dos 47 Subcomandos
- Um exemplo por subcomando, cada um executado contra o `atomwrite 0.1.38` (o comando, o resumo de uma linha e o exemplo)
- Preparação: `WS` é o caminho absoluto de um workspace vazio que contém `src/a.rs` (um `unwrap()` e um comentário), `src/b.rs` (a palavra `beta` uma vez), `src/c.rs` (com `user_id`), `src/tmp.rs`, `config.toml` (uma tabela `[package]` com `name` e uma tabela `[features]` com `x`), `notes.txt`, `tmp.txt`, `base.txt`, `ours.txt`, `theirs.txt`, `img.png` e `rules.yaml` (uma lista YAML com `id`, `language`, `pattern` e `rewrite`)
- `HASH_A` é o `checksum` que `hash src/a.rs` imprime
- Rode-os com `HOME` e as variáveis `XDG_*` apontando para dentro do workspace, como o `common::hermetic` faz na suíte de testes, para não tocar em nenhuma config, preferência de locale ou arquivo de completion reais
- Os exemplos rodam na ordem abaixo: o `rollback` precisa do `backup` anterior e o `edit-loop` precisa do `edit` anterior
- Resultado: 46 dos 47 saem 0, e o `watch` sai 78 porque o build padrão não tem a feature `watch`. Um build com `--features watch` não foi executado

- `read` — Lê um arquivo com checksum, tamanho e linhas, com opção de só início, fim ou faixa. Exemplo: `atomwrite --workspace "$WS" read --head 2 src/a.rs`
- `write` — Cria ou sobrescreve um arquivo de forma atômica a partir do stdin. Exemplo: `printf 'x\n' | atomwrite --workspace "$WS" write notes/new.txt`
- `edit` — Edita um arquivo por texto exato ou aproximado, número de linha ou marcador. Exemplo: `atomwrite --workspace "$WS" edit src/b.rs --old beta --new gamma`
- `delete` — Apaga arquivos ou árvores com backup, ou só mostra o plano. Exemplo: `atomwrite --workspace "$WS" delete --plan tmp.txt`
- `copy` — Copia um arquivo ou árvore com verificação de checksum. Exemplo: `atomwrite --workspace "$WS" copy src/a.rs src/a_copy.rs`
- `move` — Move ou renomeia um arquivo de forma atômica. Exemplo: `atomwrite --workspace "$WS" move src/tmp.rs src/tmp2.rs`
- `apply` — Aplica um patch (diff unificado, SEARCH/REPLACE, arquivo inteiro) vindo do stdin. Exemplo: `printf 'new body\n' | atomwrite --workspace "$WS" apply --format full notes.txt`
- `search` — Busca conteúdo de arquivos em paralelo com o motor do ripgrep. Exemplo: `atomwrite --workspace "$WS" search alpha src/`
- `replace` — Troca texto em vários arquivos com escrita atômica e casamento aproximado. Exemplo: `atomwrite --workspace "$WS" replace --dry-run alpha omega src/`
- `transform` — Reescreve código por padrão de AST (ast-grep). Exemplo: `atomwrite --workspace "$WS" transform --dry-run -p '$E.unwrap()' -r '$E?' -l rust src/`
- `hash` — Mostra o checksum BLAKE3 de arquivos. Exemplo: `atomwrite --workspace "$WS" hash src/a.rs`
- `count` — Conta linhas e arquivos por extensão ou por tamanho. Exemplo: `atomwrite --workspace "$WS" count --by-extension src/`
- `diff` — Compara dois arquivos (unificado, estatística ou mudanças). Exemplo: `atomwrite --workspace "$WS" diff src/a.rs src/b.rs --unified`
- `list` — Lista uma árvore de diretórios com metadados. Exemplo: `atomwrite --workspace "$WS" list --long --depth 1 src/`
- `extract` — Extrai campos de NDJSON lido do stdin. Exemplo: `printf '{"a":1}\n' | atomwrite --workspace "$WS" extract --field a`
- `extract-artifact` — Sinal limitado dos cabeçalhos de PDF, OOXML e imagem (nunca os bytes brutos). Exemplo: `atomwrite --workspace "$WS" extract-artifact img.png`
- `scope` — Escopo gramatical: age sobre comentários, strings, funções e outras categorias de código. Exemplo: `atomwrite --workspace "$WS" scope src/ --language rust --query comments --delete --dry-run`
- `regex` — Gera uma regex a partir de exemplos. Exemplo: `atomwrite --workspace "$WS" regex abc-12 xyz-99 id-7 --digits`
- `calc` — Cálculo e conversão de unidades. Exemplo: `atomwrite --workspace "$WS" calc "2 hours to seconds"`
- `completions` — Gera ou instala completions de shell (bash, zsh, fish, elvish, powershell). Exemplo: `atomwrite --workspace "$WS" completions bash`
- `backup` — Cria backups com data e hora e checksum BLAKE3. Exemplo: `atomwrite --workspace "$WS" backup src/a.rs --retention 3`
- `rollback` — Restaura um arquivo a partir de um backup anterior. Exemplo: `atomwrite --workspace "$WS" rollback src/a.rs --latest --verify`
- `batch` — Executa operações NDJSON do stdin, com opção de transação única. Exemplo: `printf '{"op":"write","target":"batch.txt","content":"x"}\n' | atomwrite --workspace "$WS" batch --transaction`
- `edit-loop` — Aplica N pares antigo/novo numa escrita só, tudo ou nada. Exemplo: `printf '[{"old":"gamma","new":"delta"}]\n' | atomwrite --workspace "$WS" edit-loop src/b.rs`
- `prune-backups` — Remove arquivos .bak antigos por idade ou quantidade. Exemplo: `atomwrite --workspace "$WS" prune-backups --max-age-secs 86400 --dry-run --recursive .`
- `wal-stats` — Mostra o estado dos journals WAL (só leitura). Exemplo: `atomwrite --workspace "$WS" wal-stats`
- `wal-heal` — Remove journals WAL terminais órfãos mais antigos que um limite. Exemplo: `atomwrite --workspace "$WS" wal-heal --threshold-secs 3600 .`
- `verify` — Confere um arquivo contra um hash BLAKE3 esperado (exit 0 se igual, 81 se diferente). Exemplo: `atomwrite --workspace "$WS" verify src/a.rs HASH_A`
- `set` — Grava um valor num caminho com pontos em arquivo TOML ou JSON. Exemplo: `atomwrite --workspace "$WS" set config.toml package.name demo2`
- `get` — Lê um valor num caminho com pontos em arquivo TOML ou JSON. Exemplo: `atomwrite --workspace "$WS" get config.toml package.name`
- `del` — Remove uma chave de um arquivo TOML ou JSON. Exemplo: `atomwrite --workspace "$WS" del config.toml features.x`
- `case` — Renomeia identificadores entre estilos de caixa em vários arquivos. Exemplo: `atomwrite --workspace "$WS" case --subvert user_id UserId --to pascal --dry-run src/c.rs`
- `query` — Percorre a AST do tree-sitter e emite os nós. Exemplo: `atomwrite --workspace "$WS" query --kinds src/a.rs`
- `outline` — Emite a estrutura de alto nível de um arquivo de código. Exemplo: `atomwrite --workspace "$WS" outline src/a.rs`
- `semantic-merge` — Merge de três vias por linha com marcadores de conflito (não é AST). Exemplo: `atomwrite --workspace "$WS" semantic-merge --base base.txt --ours ours.txt --theirs theirs.txt --output merged.txt --fail-on-conflict`
- `sparse` — Lista, leitura e outline com orçamento para árvores grandes. Exemplo: `atomwrite --workspace "$WS" sparse list src/ --max-files 10 --max-bytes 1048576`
- `recipe` — Lista e roda receitas embutidas de vários passos. Exemplo: `atomwrite --workspace "$WS" recipe run --name search-replace-verify --pattern alpha --replacement omega --path src --dry-run`
- `stat` — Só metadados, alias de read --stat. Exemplo: `atomwrite --workspace "$WS" stat src/a.rs`
- `agent-surface` — Manifesto de ferramentas para agentes (a fonte única dos subcomandos). Exemplo: `atomwrite --workspace "$WS" agent-surface --format json`
- `watch` — Transmite eventos do sistema de arquivos com debounce e termina com watch_summary (exige build com `--features watch`, o build padrão sai 78). Exemplo: `atomwrite --workspace "$WS" watch src/ --max-events 1 --idle-exit-ms 500`
- `codemod` — Aplica uma lista YAML de regras de reescrita por AST numa árvore. Exemplo: `atomwrite --workspace "$WS" codemod --rules rules.yaml --dry-run src/`
- `semantic-search` — Ranking offline por Jaccard de arquivos para uma consulta. Exemplo: `atomwrite --workspace "$WS" semantic-search "unwrap value" src/ --k 5 --min-score 0.01`
- `doctor` — Diagnóstico de prontidão do host. Exemplo: `atomwrite --workspace "$WS" doctor`
- `scratch` — Cria o diretório de staging .atomwrite/scratch dentro do jail do workspace. Exemplo: `atomwrite --workspace "$WS" scratch init .atomwrite/scratch`
- `locale` — Mostra, define ou limpa o idioma da interface. Exemplo: `atomwrite --workspace "$WS" locale`
- `commands` — Imprime a árvore completa de comandos em JSON. Exemplo: `atomwrite --workspace "$WS" commands --include-globals`
- `config` — Lê e grava chaves de ajuste persistentes (43 chaves, escopos global e workspace). Exemplo: `atomwrite --workspace "$WS" config get fuzzy.threshold`


## O Que Há de Novo na v0.1.36
- Histórico (v0.1.36): CAPA residual de contrato de agente, erros always-emit sob `--filter`; extract `--field`; `CANONICAL_SUBCOMMAND_COUNT` era 46
- Histórico (v0.1.36): a superfície completa da CLI sob política de teste era de 46 subcomandos (`CANONICAL_SUBCOMMAND_COUNT`): `agent-surface`, `apply`, `backup`, `batch`, `calc`, `case`, `codemod`, `commands`, `completions`, `config`, `copy`, `count`, `del`, `delete`, `diff`, `doctor`, `edit`, `edit-loop`, `extract`, `get`, `hash`, `list`, `locale`, `move`, `outline`, `prune-backups`, `query`, `read`, `recipe`, `regex`, `replace`, `rollback`, `scope`, `scratch`, `search`, `semantic-merge`, `semantic-search`, `set`, `sparse`, `stat`, `transform`, `verify`, `wal-heal`, `wal-stats`, `watch`, `write`
- A suíte `tests/cli_v0136_gaps.rs` cobre pontos de contrato GAP-019…033 (jail do workspace / staging scratch, erro `ok:false` + `match_count`, honestidade fuzzy com estratégias normalizadas emitindo `similarity: null` (nunca 1.0 falso), política dual-gate / soft-floor, flags de shape, extract `--field` e não `--select`, fórmulas de staging)
- Gate SSOT local: o inventário de gaps é nota local não versionada, então o status corrente de cada gap vive na série de ADRs e a revisão mantém os dois alinhados
- Rode: `cargo test --test cli_v0136_gaps`
- DoD local (só gates locais): `cargo test --lib --tests`, `cargo clippy --all-targets -- -D warnings`
- Pin histórico `^0.1.36`; ADR-0055


## O Que Há de Novo na v0.1.35
- Residuais A-* fechados: suite verde sem `delete --yes`; sobrescrita grande default-deny (`--ack-overwrite`); `watch` sempre emite `watch_summary`; markers de semantic-merge default ON; monólitos fatiados com `include!`
- DoD local (só gates locais): `cargo test --lib --tests`, `cargo clippy --all-targets -- -D warnings`, `cargo check --target x86_64-pc-windows-gnu`, `cargo install --path . --force`
- Suite de contrato: `cargo test --test cli_e2e_v0135`
- Veja o ADR-0055 para a matriz de closeout A-*


## O Que Há de Novo na v0.1.34
- A suíte `tests/cli_v0133_oneshot_fuzzy.rs` trava o contrato one-shot fuzzy + timeout (fix de código registrado como 0.1.33; publicação docs-complete 0.1.34)
- Cobre: hang de expand-section quando NEW embute OLD termina em menos de 2 segundos; embeds forçam apply único via `apply_fuzzy_one_pass`; teto rígido 10_000 applies; timeout global padrão 120 / exit 124; cancel cooperativo no meio da cascata / exit 143
- Contrato explícito sob teste: `apply_fuzzy_one_pass` one-pass E→D (nunca reescaneia texto inserido); NEW embutindo OLD não pode hangar
- Rode: `cargo test --test cli_v0133_oneshot_fuzzy`
- Mantenha as suítes mais antigas listadas abaixo
- Veja [ADR-0054](decisions/0054-v0-1-34-oneshot-fuzzy-timeout.pt-BR.md)


## O Que Há de Novo na v0.1.30
- A suíte `tests/cli_v0130_agent_contract.rs` cobre o contrato residual: match_count, indent_adjusted, modos fuzzy, recipe bak skip, sparse outline kind, semantic-merge help line-based, unicidade replace-all. Nota (v0.1.35): `--fuzzy off` = exact-only (G-010), não rejeitado
- A suíte `tests/cli_v0129_fuzzy_replace.rs` cobre a superfície 0.1.29: replace fuzzy, `best_candidate`, durability, recipe, sparse, semantic-merge, stat, agent-surface, semantic-search, `platform.rename_method`
- `replace --fuzzy auto|aggressive|off` (`off` = exact-only, G-010; padrão `auto`) com `--fuzzy-threshold` opcional e `--progress-every` em massa
- Envelopes de falha de match podem incluir `best_candidate` (schema `best-candidate.schema.json`)
- Cancel cooperativo emite eventos NDJSON `cancelled` (schema `cancelled-event.schema.json`, exit 143)
- O NDJSON do write reporta `platform.rename_method` (`renameat2` ou `rename`)
- `write --durability full|fast|auto` para trade-off de custo de fsync
- Histórico (na v0.1.29): novos subcomandos: `semantic-merge`, `sparse`, `recipe`, `stat`, `agent-surface`, `watch` (feature), `codemod`, `semantic-search` — 41 subcomandos no total
- Matriz de features: slim `core` (26.560.352 bytes = 25,33 MiB medidos em 2026-11-04, acima do size-gate local de 15 MiB) vs default AST (71.084.440 bytes = 67,79 MiB medidos em 2026-11-04)
- Gates locais (manuais; sem scripts locais remoto): size-gate slim ≤15 MiB, cujo build slim medido em 2026-11-04 é 25,33 MiB e fica acima dele; core-test; schema-diff em `docs/schemas`
- Rode a suíte de contrato 0.1.30: `cargo test --test cli_v0130_agent_contract`
- Rode a suíte 0.1.29: `cargo test --test cli_v0129_fuzzy_replace`


## O Que Há de Novo na v0.1.28
- 661 testes passando, 0 falhas, 3 ignorados (gate de cross-compile)
- BREAKING: `delete` agora cria backup por padrão (era opt-in) — passe `--no-backup` para desabilitar
- BREAKING: `move`/`copy` sobrescrevendo um destino existente agora exigem `--force` OU um `--backup` explícito
- GAP-CLI-SURFACE-DRIFT resolvido: struct única `BackupOpts` achatada via `#[command(flatten)]` em 15 subcomandos mutantes, substituindo 15 conjuntos de flags divergentes declarados manualmente
- GAP-CONFIG-DEFAULTS-DEAD resolvido: chaves `backup`/`retention` de `[defaults]` do `.atomwrite.toml` agora são efetivas de ponta a ponta via uma única chamada `resolve_backup()` alimentada por `load_config()` em `lib.rs::run()`
- Novo guard compartilhado de stdin-tty no `edit`: 7 modos consumidores de stdin falham rápido com exit 65 `INVALID_INPUT` em vez de bloquear indefinidamente quando stdin é um terminal
- Novos arquivos de teste: `cli_v0128_backup_matrix.rs` (12 testes), `cli_v0128_config_defaults.rs` (6 testes), `cli_v0128_edit_stdin_guard.rs` (3 testes), `cli_v0128_batch_backup.rs` (6 testes)
- 33 subcomandos, 32 ADRs (0019-0050)


## O Que Há de Novo na v0.1.27
- 631+ testes passando, 0 falhas, 3 ignorados (gate de cross-compile)
- 10 bugs corrigidos: BUG-SEC-001 (CRÍTICO escape via symlink-dir), BUG-SCOPE-004 (CRÍTICO scope --delete destrói código), BUG-GET, BUG-001, BUG-002, BUG-005, BUG-008, BUG-SCOPE-002, BUG-SCOPE-003, BUG-SCOPE-005
- 3 limitações conhecidas documentadas: GAP-01 (test-fn), GAP-02 (doc-comment), GAP-03 (JS export)
- `tests/cli_get_del.rs` atualizado: `get` chave ausente espera exit 65 INVALID_INPUT (era exit 4)
- Teste unitário `lookup_rust_queries_known` do `scope.rs` atualizado para padrões com pub
- Teste unitário `lookup_go_queries_known` do `scope.rs` atualizado para var sem tipo
- 33 subcomandos, 29 ADRs (0019-0047)


## O Que Há de Novo na v0.1.25
- 631 testes passando, 0 falhas, 3 ignorados (gate de cross-compile)
- 49 gaps adicionais resolvidos em 6 rodadas de auditoria e2e (GAP-071 a GAP-134)
- Testes property-based de fuzzy via proptest (5 propriedades em `tests/proptest_fuzzy.rs`)
- ~505 cenários e2e executados contra binário real


## O Que Há de Novo na v0.1.24
- 621 testes passando, 0 falhas, 3 ignorados (gate de cross-compile)
- 52 gaps resolvidos (GAP-2026-019 a GAP-2026-070)
- 20 `anyhow::bail!()` convertidos para `AtomwriteError` tipado (testes unitários inline por comando)
- 3 novos ADRs (0045-0047)

### Novos Arquivos de Teste (v0.1.24)
- `tests/cli_v0124_clap_suggestion.rs` — 5 testes para GAP-019 (sugestões acionáveis de erro clap)
- `tests/cli_gap020_diff_workspace.rs` — 3 testes para GAP-020 (diff resolve-first)
- `tests/cli_gap021_scope_readonly.rs` — 4 testes para GAP-021 (scope modo read-only)

### Correções Validadas por Suítes Existentes
- GAP-022 a GAP-070: correções validadas por testes de integração existentes nos arquivos `cli_*.rs`
- Cada conversão `bail!` → `AtomwriteError` inclui asserções `#[cfg(test)]` inline
- `cargo test` confirma 621 total: 609 (v0.1.23) + 5 (GAP-019) + 3 (GAP-020) + 4 (GAP-021)


## O Que Há de Novo na v0.1.23
- 609 testes passando (575 baseline v0.1.22 + 12 GAP-2026-015 + 7 GAP-2026-016 + 4 GAP-2026-017 + 8 GAP-2026-018 + 3 de [Unreleased] Windows)
- 33 subcomandos (32 da v0.1.22 + `verify` da v0.1.25)
- 26 ADRs em docs/decisions/ (0019-0044)
- GAP-2026-015 (allow_hyphen_values) fechado; 12 campos CLI em 8 structs aceitam valores iniciando com `-`, e os três posicionais exigem `--` desde a 0.1.37
- GAP-2026-016 (backup-by-default) fechado; backup habilitado por padrão em 9 structs que mutam conteúdo
- GAP-2026-017 (guarda de shrink) fechado; writes que reduzem >50% bloqueados quando --expect-checksum ativo
- GAP-2026-018 (--old-file/--new-file) fechado; edit lê match/substituição de arquivos, contornando ARG_MAX
- Novos testes: `tests/cli_v0123_hyphen_values.rs` (12), `tests/cli_v0123_backup_default.rs` (7), `tests/cli_v0123_shrink_guard.rs` (4), `tests/cli_v0123_old_file.rs` (8)


## O Que Há de Novo na v0.1.22: Totais de Teste
- 631+ testes passando (542 baseline v0.1.18 + 33 na v0.1.21-v0.1.22 + 46 na v0.1.23-v0.1.24 + 10 na v0.1.25)
- 33 subcomandos (30 baseline v0.1.18 + `edit-loop` + `prune-backups` da v0.1.22 + `verify` da v0.1.25)
- 22 ADRs em docs/decisions/ (0019-0040)
- 2 novos schemas NDJSON: `edit-loop-output.schema.json`, `prune-backups-output.schema.json`


## O Que Há de Novo na v0.1.21
- GAP-2026-012 (flag `--allow-sequential-drift`) fechado; edits sequenciais não exigem mais re-capturar checksum entre iterações
- GAP-2026-013 Frente 4 (`--backup` exposto em `edit` e `rollback`) fechado; paridade com `write` e `replace`
- GAP-2026-014 v2 (backup é deletado por padrão após sucesso) fechado; flag opt-in `--keep-backup` adicionada
- ADR-0038 criado documentando o paradigma de backup que deleta após sucesso
- Novos testes: `tests/cli_v0121_sequential_drift.rs` (4), `tests/cli_v0121_backup_keep_flag.rs` (3), `tests/cli_v0121_edit_backup.rs` (3), `tests/cli_v0121_rollback_backup.rs` (2), `tests/cli_v0121_apply_keep.rs` (2), `tests/cli_v0121_batch_keep.rs` (2), `tests/proptest_v0121_backup_delete.rs` (2 property tests)


## O Que Há de Novo na v0.1.22
- GAP-2026-012 Frente 3 (sub-comando helper `edit-loop`) fechado; novo `edit-loop` aplica N pares `{old, new}` em 1 invocação via NDJSON no stdin
- GAP-2026-013 Frente 2 (sub-comando `prune-backups`) fechado; novo `prune-backups` oferece limpeza manual de arquivos `.bak.YYYYMMDD_HHMMSS` legados
- ADR-0039 criado documentando o design de `edit-loop`
- ADR-0040 criado documentando o design de `prune-backups`
- Novos testes: `tests/cli_v0121_edit_loop.rs` (4), `tests/cli_v0121_prune_backups.rs` (3)


## O Que Há de Novo na v0.1.12 (histórico)

Esta seção resume as mudanças relevantes para testes em v0.1.12. A release adicionou 96 novos testes (eram 320 baseline) para um total de 445 testes em 43 suítes. A v0.1.15 eleva o total para 542 testes (+2 testes unitários na v0.1.14, +8 do G117 em `tests/cli_edit.rs`, +6 do G118 em `tests/cli_write.rs`).

### Novos Arquivos de Teste (10 Adicionados em v0.1.11+v0.1.12)
- `tests/cli_v012_regressions.rs` -- 11 testes para regressões v0.1.2 a v0.1.4
- `tests/cli_v012_audit_regressions.rs` -- 27 testes para auditoria G72/G114 de v0.1.12
- `tests/cli_v012_batch4_regressions.rs` -- 23 testes para o batch final de v0.1.12
- `tests/cli_v012_syntax_check.rs` -- 5 testes para validação G72 tree-sitter
- `tests/cli_v012_wal.rs` -- 8 testes para G114 sidecar WAL
- `tests/cli_v012_xattr_reflink.rs` -- 3 testes para G39 xattr + G64 reflink
- `tests/cli_set.rs` -- 6 testes para subcomando set v14 Tier 3
- `tests/cli_get_del.rs` -- 5 testes para subcomandos get/del v14 Tier 3
- `tests/cli_query.rs` -- 5 testes para subcomando query v14 Tier 3
- `tests/cli_outline.rs` -- 5 testes para subcomando outline v14 Tier 3
- `tests/cli_case.rs` -- 3 testes para subcomando case v14 Tier 3

### Novos Testes em Arquivos Existentes
- 12 testes em `src/binary_detect.rs::tests` (G41 content_inspector)
- 16 testes em `src/syntax_check.rs::tests` (G72 tree-sitter)
- 8 testes em `src/wal.rs::tests` (G114 WAL)
- 3 testes em `src/xattr_restore.rs::tests` (G39 xattr)
- 3 testes em `src/lock.rs::tests` (G54 advisory lock)
- 3 testes em `src/atomic.rs::tests` (WriteStrategy: rename/inplace/copyback)

### Organização das Suítes de Teste
- Total: 43 suítes (eram 34 em v0.1.10)
- Testes unitários em `src/`
- Testes de integração em `tests/cli_*.rs`
- Testes property-based em `tests/proptest_*.rs`
- Testes de snapshot via `insta`
- Testes de sinal (SIGINT, SIGTERM, SIGPIPE)


### Como Executar

```bash
# Executar todos os testes
cargo test

# Executar a suíte one-shot fuzzy + timeout v0.1.33/v0.1.34
cargo test --test cli_v0133_oneshot_fuzzy
# Executar a suíte de contrato residual v0.1.30
cargo test --test cli_v0130_agent_contract
# Executar a suíte v0.1.29 (replace fuzzy + nova superfície)
cargo test --test cli_v0129_fuzzy_replace

# Executar apenas a suíte de regressão v0.1.12
cargo test --test cli_v012_regressions
cargo test --test cli_v012_audit_regressions
cargo test --test cli_v012_batch4_regressions

# Execução suportada: a suíte completa com as features padrão
cargo test --locked

# Build slim: só prova que compila (o xtask existe só num checkout do código-fonte)
cargo xtask check-features

# Executar com output visível para debug
cargo test --test cli_v012_syntax_check -- --nocapture

# Executar o gate de cross-compile
cargo test --test cross_compile_check -- --ignored
```

### Cobertura
- Histórico (na v0.1.12): 20.19% de cobertura de linhas via `cargo tarpaulin` (935/4631 linhas cobertas), medido naquela versão e não repetido desde então
- Ficou baixa porque o tarpaulin conta apenas testes unitários, não testes de integração CLI
- A suíte de testes de integração é a métrica primária de cobertura (veja Estatísticas Atuais)

### Dependências Adicionadas
- `tree-sitter-language-pack = "1.8"` -- 305 linguagens para query/outline/syntax-check naquela versão, e a árvore hoje fixa a 1.22.1 com 372
- Todas as dependências de teste já estão na seção dev-dependencies

### ADRs e Schemas
- 29 ADRs em `docs/decisions/` (0019-0047) explicam as decisões arquiteturais por trás das features v0.1.12 a v0.1.27
- Histórico (na v0.1.27): 29 schemas JSON em `docs/schemas/`; a contagem atual é 54 (índice completo em `docs/schemas/README.md`)
- Veja [docs/decisions/README.md](decisions/README.md) para a lista completa de ADRs


## Por Que Testes Categorizados
- Cada categoria de teste valida uma camada diferente do sistema
- Testes unitários verificam lógica pura em isolamento
- Testes de integração verificam comportamento da CLI de ponta a ponta
- Testes de snapshot travam a estrutura de saída JSON
- Testes property-based descobrem edge cases que humanos perdem
- Executar todas as categorias juntas dá confiança de que atomwrite funciona corretamente


## Estatísticas Atuais
- Execução final da v0.1.38 registrada no `CHANGELOG.md`: 131 alvos de teste, 1486 testes passaram, 0 falharam e 9 ignorados (os testes `cross_compile_*`)
- Inventário medido com `fd` e `rg` na árvore de código: 131 alvos de teste de integração em `tests/` (mais `tests/common/mod.rs`, `doc_scan.rs` e `schema_check.rs`), 994 funções `#[test]` em `tests/` e 485 em `src/` (DERIVADO: 994 + 485 = 1479), mais 9 arquivos de snapshot, 4 arquivos de proptest e 2 alvos de fuzzing
- Meça de novo com `rg -c '#\[test\]' tests src` e `cargo test --lib --tests`. Um teste gerado por macro (proptest) não entra nessa contagem do `rg`, então o número de testes que rodam pode diferir do número de atributos `#[test]`
- Suítes por tema: unitários + integração + snapshot + property-based + sinal + tracing + NDJSON + regressão + cross-compile + concorrência
- Histórico (v0.1.11 e v0.1.12): 96 novos testes foram adicionados então:
  - 11 testes em `tests/cli_v012_regressions.rs` (fixes GAP 13, GAP 14, GAP 18)
  - 27 testes em `tests/cli_v012_audit_regressions.rs` (auditoria v0.1.12 G72/G114)
  - 23 testes em `tests/cli_v012_batch4_regressions.rs` (batch final v0.1.12)
  - 5 testes em `tests/cli_v012_syntax_check.rs` (G72 tree-sitter)
  - 8 testes em `tests/cli_v012_wal.rs` (G114 WAL sidecar)
  - 3 testes em `tests/cli_v012_xattr_reflink.rs` (G39 xattr + G64 reflink)
  - 6 testes em `tests/cli_set.rs` (v14 Tier 3)
  - 5 testes em `tests/cli_get_del.rs` (v14 Tier 3)
  - 5 testes em `tests/cli_query.rs` (v14 Tier 3)
  - 5 testes em `tests/cli_outline.rs` (v14 Tier 3)
  - 3 testes em `tests/cli_case.rs` (v14 Tier 3)
  - 16 testes em `src/syntax_check.rs::tests` (G72 tree-sitter)
  - 8 testes em `src/wal.rs::tests` (G114 WAL)
  - 12 testes em `src/binary_detect.rs::tests` (G41 content_inspector)
  - 3 testes em `src/xattr_restore.rs::tests` (G39)
  - 3 testes em `src/lock.rs::tests` (G54 advisory lock)
- 2 novos testes de regressão de mtime em `src/atomic.rs::tests` (v0.1.3)
- 7 testes de sugestão de erro do GAP 13 em `src/error.rs::tests` (v0.1.4)
- 3 testes do gate de cross-compile em `tests/cross_compile_check.rs` (v0.1.4; hoje são 9)
- 9 arquivos de snapshot em `tests/snapshots/`
- 2 arquivos de regressão proptest
- 2 alvos de fuzzing em `fuzz/fuzz_targets/`


## Categorias de Teste
### Testes Unitários (src/)
- Localizados dentro dos arquivos fonte via módulos `#[cfg(test)]`
- Testam funções puras sem interação com filesystem ou CLI
- `path_safety.rs` -- 5 testes para prevenção de path traversal e workspace jail
- `checksum.rs` -- correção do hashing BLAKE3

### Testes de Integração (tests/cli_*.rs)
- Localizados no diretório `tests/`
- Cada arquivo testa um subcomando de ponta a ponta via binário compilado
- Usam `assert_cmd` para invocar atomwrite como subprocesso
- Usam a crate `tempfile` para fixtures isoladas de filesystem
- `cli_write.rs` -- 6 testes para operações de escrita atômica
- `cli_read.rs` -- 6 testes para leitura com metadados e checksums
- `cli_edit.rs` -- 5 testes para modos de edição cirúrgica
- `cli_search.rs` -- 6 testes para busca paralela
- `cli_replace.rs` -- 6 testes para substituição de texto em massa
- `cli_hash.rs` -- 4 testes para operações de checksum BLAKE3
- `cli_delete.rs` -- 4 testes para deleção de arquivo com backup
- `cli_count.rs` -- 2 testes para contagem de linhas e arquivos
- `cli_diff.rs` -- 4 testes para comparação de arquivos
- `cli_move.rs` -- 5 testes para move e rename atômico
- `cli_copy.rs` -- 4 testes para cópia com verificação de checksum
- `cli_list.rs` -- 4 testes para listagem de estrutura do projeto
- `cli_extract.rs` -- 4 testes para extração de campos NDJSON
- `cli_calc.rs` -- 5 testes para matemática e conversão de unidades
- `cli_regex.rs` -- 5 testes para geração de regex a partir de exemplos
- `cli_transform.rs` -- 5 testes para busca e reescrita AST
- `cli_scope.rs` -- operações de escopo: encontrar funções, deletar comentários, erro de linguagem desconhecida, padrões customizados
- `cli_backup.rs` -- criação de backup, dry-run, erro de arquivo não encontrado, múltiplos arquivos
- `cli_rollback.rs` -- restauração de backup, dry-run, erro sem backup disponível, flag de verificação
- `cli_apply.rs` -- substituição completa de arquivo, blocos SEARCH/REPLACE, diff unificado, dry-run
- `cli_batch.rs` -- 13 testes para execução de operações em batch (write, replace, delete, move, copy, alias path, rollback transacional)

### Testes de Snapshot (insta)
- Localizados em `tests/snapshot_write.rs` -- 9 testes
- Usam a crate `insta` para verificação de saída baseada em snapshot
- Travam a estrutura JSON exata de cada tipo de saída
- Arquivos de snapshot armazenados em `tests/snapshots/`
- Execute `cargo insta review` para aceitar ou rejeitar mudanças de snapshot
- Snapshots disponíveis:
- `snapshot_write__write_output_structure.snap`
- `snapshot_write__read_output_structure.snap`
- `snapshot_write__edit_output_structure.snap`
- `snapshot_write__search_match_structure.snap`
- `snapshot_write__replace_result_structure.snap`
- `snapshot_write__error_not_found_structure.snap`
- `snapshot_write__batch_summary_structure.snap`
- `snapshot_write__error_invalid_input_structure.snap`
- `snapshot_write__error_workspace_jail_structure.snap`

### Testes de Regressão
- `tests/cli_v012_regressions.rs` -- 11 testes para regressões de v0.1.2 a v0.1.4
- `jail_suggestion_mentions_workspace_flag` -- atualizado na v0.1.4 (GAP 13): afirma que a sugestão menciona `--workspace` quando nenhum workspace é fornecido
- `gap13_jail_suggestion_when_workspace_supplied_says_inside` -- adicionado na v0.1.4 (GAP 13): afirma que a sugestão diz "inside the workspace" quando `--workspace` É fornecido
- Outras regressões cobrem scope, batch path/source aliases, fuzzy edit, search files dedup, edit multi NDJSON, clap JSON errors, json-schema, validate path jail, dead flags, mtime

### Gate de Cross-Compile (v0.1.4)
- `tests/cross_compile_check.rs` -- 9 testes (3 protegiam compilação só de Windows na v0.1.4, 6 a mais cobrem Windows arm64, Linux musl e aarch64, Apple aarch64 e x64)
- Eles NÃO foram validados na v0.1.37: o host da auditoria não tem os linkers estrangeiros (por exemplo `x86_64-w64-mingw32-gcc`)
- Os 9 testes são `cross_compile_windows_gnu_x64_succeeds`, `cross_compile_windows_gnu_i686_succeeds`, `cross_compile_windows_msvc_succeeds`, `cross_compile_windows_arm64_msvc_succeeds`, `cross_compile_linux_musl_x64_succeeds`, `cross_compile_linux_aarch64_gnu_succeeds`, `cross_compile_linux_aarch64_musl_succeeds`, `cross_compile_apple_aarch64_succeeds` e `cross_compile_apple_x64_succeeds`
- `cross_compile_windows_gnu_x64_succeeds` -- afirma que `cargo check --target x86_64-pc-windows-gnu` sucede e não emite E0433, E0308, ou E0507
- `cross_compile_windows_gnu_i686_succeeds` -- afirma o mesmo para `i686-pc-windows-gnu` (Windows 32-bit)
- `cross_compile_windows_msvc_succeeds` -- afirma o mesmo para `x86_64-pc-windows-msvc` (toolchain Microsoft Visual C++)
- Todos os testes são `#[ignore]` por padrão para pular em hosts sem os targets Windows instalados
- Execute com `cargo test --test cross_compile_check -- --ignored`
- Pula graciosamente quando o linker necessário (lib.exe para MSVC, i686-w64-mingw32-gcc para 32-bit) está ausente
- Obrigatório antes de qualquer release que toque código `#[cfg(windows)]` (veja `docs/INSTALL.md` e `docs/CROSS_PLATFORM.md`)

### Testes Property-Based (proptest)
- Localizados em `tests/proptest_checksum.rs` -- 2 testes
- Localizados em `tests/proptest_backup.rs` -- 2 testes
- Usam a crate `proptest` para gerar inputs aleatórios
- Verificam que checksums BLAKE3 são determinísticos para qualquer input
- Verificam que criação de backup funciona para conteúdos arbitrários de arquivo
- Arquivos de regressão armazenados ao lado dos arquivos de teste

### Testes de Sinal
- Localizados em `tests/signal_test.rs` -- 5 testes
- `sigpipe_handler_ends_the_run_with_exit_141` prova que um pipe fechado encerra a execução com exit 141 (o SIGPIPE tem handler cooperativo, não `SIG_DFL`)
- `sigint_during_search_exits_130` e `sigterm_during_search_exits_143` provam os exit codes dos sinais
- `batch_interrupted_by_signal` e `shutdown_message_on_stderr` provam a limpeza e a mensagem de shutdown
- Verificam comportamento de shutdown gracioso na recepção de sinais

### Testes de Validação NDJSON
- Localizados em `tests/ndjson_valid_test.rs` -- 21 testes (contados com `rg -c '#\[test\]'`)
- 19 testes validam a saída NDJSON de 19 dos 47 comandos: `search`, `hash`, `list`, `diff`, `write`, `read`, `edit`, `replace`, `count`, `backup`, `copy`, `move`, `delete`, `batch`, `calc`, `regex`, `rollback` (caso não encontrado), `apply` e `extract`
- 2 testes de interoperabilidade `jaq` (`search` e `read`) verificam parsing de JSON via pipe, e são pulados quando o `jaq` não está no `PATH`

### Testes de Concorrência
- Localizados em `tests/cli_concurrency.rs`
- Testam operações paralelas de arquivo e condições de corrida

### Testes de Tamanho Máximo de Arquivo
- Localizados em `tests/cli_max_filesize.rs`
- Testam enforcement de limites de tamanho de arquivo

### Testes de Tracing
- Localizados em `tests/tracing_test.rs`
- Verificam comportamento da infraestrutura de tracing/logging

### Alvos de Fuzzing
- Localizados em `fuzz/fuzz_targets/batch_parse.rs` -- fuzzing do parser NDJSON de batch
- Localizados em `fuzz/fuzz_targets/extract_json.rs` -- fuzzing do parser JSON de extract
- Requerem `cargo +nightly fuzz run <alvo>`
- Executar com `--max_total_time=30` para validação rápida


## Como Executar
### Executar Todos os Testes

```bash
cargo test
```

### Executar um Arquivo de Teste Específico

```bash
cargo test --test cli_write
cargo test --test cli_search
cargo test --test snapshot_write
cargo test --test proptest_checksum
```

### Executar um Teste Específico por Nome

```bash
cargo test --test cli_write test_write_atomic
cargo test --test cli_edit test_edit_old_new
```

### Executar Apenas Testes Unitários

```bash
cargo test --lib
```

### Executar Apenas Testes de Integração

```bash
cargo test --tests
```

### Executar Com Saída Visível

```bash
cargo test -- --nocapture
```

### Executar Revisão de Snapshots

```bash
cargo insta review
```

### Atualizar Todos os Snapshots

```bash
cargo insta test
cargo insta review
```


## Perfis de teste local
### Rápido (Desenvolvimento)
- Execute `cargo test` com configurações padrão
- Pule testes property longos com `PROPTEST_CASES=10`
- Adequado para hooks de pre-commit
- Tempo total: estimativa antiga de menos de 30 segundos, não medida na v0.1.37

### Slim (só verificação de compilação)
- `cargo test --no-default-features --features core` compila mas NÃO fica verde: 44 testes em 11 alvos (`cli_scope`, `cli_transform`, `cli_query`, `cli_outline`, `cli_concurrency`, `cli_e2e_v0135` e outros) exigem a feature `ast` e não têm `#[cfg(feature = "ast")]` (`gaps.md` W19, `CHANGELOG.md` Notes)
- Use `cargo xtask check-features` para provar que o build slim compila: ele roda `cargo check --locked` nos conjuntos `core` e `core,watch` e para na primeira falha
- Rode a suíte de testes com as features padrão

### Completo (pré-release local v0.1.36+)

Gate local obrigatório de release (só gates locais):

```bash
cargo fmt --check
cargo clippy --all-targets -- -D warnings
cargo test --lib
cargo test --tests
cargo test --test cli_v0136_gaps
```

### Completo (pré-release local)
- Execute `cargo test` com todas as features
- Execute `cargo clippy -- -D warnings` para linting
- Execute `cargo fmt -- --check` para formatação
- Execute `cargo insta test` para verificação de snapshots
- Execute testes property com contagem padrão de cases
- Adequado para validação de pull request
- Tempo total: estimativa antiga de menos de 2 minutos, não medida na v0.1.37

### Release (Pré-Publicação)
- Execute `cargo test --release` para testes com build otimizado
- Execute `cargo test --test proptest_checksum` com `PROPTEST_CASES=1000`
- Execute `cargo test --test proptest_backup` com `PROPTEST_CASES=1000`
- Verifique que todos os snapshots estão atualizados com `cargo insta test`

### Performance (somente local)
Rules Rust eficiência + latência: medir antes de otimizar; nunca comparar
debug com release; preferir percentis de cauda (P50/P99/P999/P9999) à média.

```bash
# Microbenchmarks Criterion (hash + read/mmap). Profile herda release (LTO fat, CGU=1).
# Cada bench também imprime `latency_hist … P50=…ns P99=…ns P999=…ns P9999=…ns` no stderr.
cargo bench --bench hash_benchmark
cargo bench --bench read_benchmark

# Binário opcional focado em tamanho (mesmo LTO/strip do release, opt-level = "z")
cargo build --profile min-size

# Binário com CPU do host só para profiling local (não publicar):
RUSTFLAGS='-C target-cpu=native' cargo build --release

# Profile de hot path (instale samply ou cargo-flamegraph à parte):
# samply record ./target/release/atomwrite search PATTERN --path .
# cargo flamegraph --bin atomwrite -- search PATTERN --path .
```

Notas de orçamento de latência (CLI one-shot, não HFT):

| Path | Dominado por | Notas |
|------|----------------|-------|
| `hash` multi-arquivo | BLAKE3 + FS | Digests em paralelo (rayon), NDJSON ordenado |
| `hash`/`read` arquivo grande | mmap + page cache | `posix_fadvise(SEQUENTIAL)` no Linux |
| stdout NDJSON | serialização + flush | Flush por linha é intencional (TTFB do agente) |
| PGO/BOLT / isolcpus / mlockall | — | N/A para CLI multi-host de vida curta |

Knobs de release em `Cargo.toml` (`[profile.release]`, `[profile.bench]`,
`[profile.min-size]`) e linker mold em `.cargo/config.toml` (Linux x86_64).
Sem gates de benchmark remotos: os benchmarks rodam só na máquina local (política do projeto).


## Logging e knobs do harness de teste
- Logging do binário é somente CLI + XDG (v0.1.35): use `-v`/`-vv`/`-vvv` e `-q`/`-qq`; tee opcional de arquivo sob o state XDG `…/atomwrite/logs` via `directories::ProjectDirs` (sem knobs de produto `ATOMWRITE_*` ou `RUST_LOG` no binário).
- Entrypoint de tracing: `init_tracing` (diagnósticos locais apenas — sem telemetria de produto).
- Apenas harness Cargo/teste (não config de produto): `PROPTEST_CASES`, `INSTA_UPDATE`, `RUST_TEST_THREADS`, `CARGO_TARGET_DIR`.
- Não documente nem dependa de variáveis de ambiente de produto para logging, locale, workspace, backup ou home override — removidas na v0.1.35 (use só CLI + XDG).
- O `doctor` não lê variável de ambiente: a detecção do host usa só sondas de arquivo; isso não configura knobs de produto.
- O helper `common::hermetic` define `HOME`, `XDG_*`, `LANG` e `LC_ALL` só no processo filho, para os testes; não é uma configuração de produto.


## Solução de Problemas
### Mismatch de Snapshot
- Execute `cargo insta review` para ver o diff
- Aceite mudanças com `cargo insta accept`
- Rejeite e corrija com `cargo insta reject`
- Snapshots ficam em `tests/snapshots/`

### Regressão de Proptest
- Verifique arquivos `tests/*.proptest-regressions` para falhas registradas
- Estes arquivos contêm inputs mínimos de reprodução
- Deletar arquivos de regressão força re-descoberta (não recomendado)
- Corrija a causa raiz

### Testes Flakey
- Verifique problemas de timing do filesystem em testes de integração
- Use `--test-threads 1` para serializar execução de testes
- Verifique se `/tmp` tem espaço suficiente para arquivos temporários

### Erros de Permissão
- Testes de integração criam diretórios temporários
- Verifique acesso de escrita a `TMPDIR` ou `/tmp`
- Alguns testes verificam comportamento de permissão negada e precisam de diretórios pai graváveis
- Use `tempfile::tempdir()` para todas as fixtures de filesystem

### Teste Trava ou Expira
- Verifique loops infinitos no código sendo testado
- Use `RUST_TEST_THREADS=1` para executar sequencialmente
- Verifique que nenhum teste escreve em path que outro teste lê

### Download de Parsers nos Testes
- Os testes de `outline`, `query` e `--syntax-check` precisam do pacote de parsers tree-sitter (23.031.807 bytes em Linux x86_64, o que dá 21,97 MiB) no `XDG_CACHE_HOME` real; a primeira execução o baixa, então rode uma vez com rede ou encha o cache antes
- Os testes nunca leem o `HOME`, a config nem a preferência de locale reais: o helper `common::hermetic` os isola, e só o cache é compartilhado

### Um Teste Depende da Máquina
- Se um teste passa numa máquina e falha em outra, confira se ele monta o comando por `common::hermetic(...)` e não lê config de desenvolvedor
- Um teste que define a própria config deve redirecionar `HOME` e `XDG_*` depois de chamar `hermetic`

### Testes Slim Falham
- `cargo test --no-default-features --features core` deve falhar 44 testes em 11 alvos que exigem a feature `ast`; rode a suíte com as features padrão (veja Perfis de teste local)

### Gate de Cross-Compile Falha
- Confirme que o target Windows está instalado: `rustup target list --installed`
- Instale targets ausentes: `rustup target add x86_64-pc-windows-gnu i686-pc-windows-gnu x86_64-pc-windows-msvc`
- No Linux, instale o toolchain necessário: `mingw64-gcc` (Fedora) ou `mingw-w64` (Ubuntu)
- Para Windows 32-bit: instale `mingw32-gcc` separadamente
- Para MSVC: instale Visual Studio 2019+ Build Tools com workload C++ (lib.exe deve estar no PATH)
- Reexecute após corrigir: `cargo test --test cross_compile_check -- --ignored`
- O gate reporta linker ausente com snippet de stderr claro como "lib.exe" ou "i686-w64-mingw32-gcc"; case essa string com o toolchain ausente


## v0.1.20 — Novidades

Esta release introduz uma nova camada de segurança chamada intention guards e renomeia a flag global `--lang` para `--locale` para desambiguar do seletor tree-sitter `--lang` usado por `scope` e `transform`.

### Intention Guards (5 flags OPT-IN)
- `--require-backup` — exige `--backup`: aborta quando falta e o arquivo alvo existe (não recebe valor)
- `--confirm` — flag legada de awareness no `write` (histórico: Y/N interativo em sobrescrita grande). Substituído na v0.1.35: sobrescrita grande é default-deny one-shot via `--ack-overwrite` (sem Y/N); `delete --confirm` é rejeitado (use `--plan`)
- `--auto-rotate` — força um backup de rotação automática para arquivos modificados há pouco (idade de `[write].auto_rotate_max_age_secs`, padrão 24 h; não recebe valor)
- `--risk-threshold <PERCENT>` — limite de variação de tamanho, em percentual, para o aviso de risco (padrão 255 = desligado; por exemplo `--risk-threshold 50`)
- `--locale <en|pt-BR>` — renomeado de `--lang` para desambiguar do `--lang` tree-sitter

### Outras Adições
- `count --by-size` — lista os maiores arquivos da árvore com tamanhos e contagem de linhas
- `read --format raw|ndjson` — seleciona entre saída byte-stream e envelope NDJSON estruturado (uma grafia antiga com `--mode` nunca existiu)
- `search --no-begin-end` — desabilita a decoração implícita de âncoras `^` e `$` na saída regex
- `write --preserve-timestamps` — preserva o mtime do arquivo fonte ao sobrescrever
- `scope --lang rust` — alias explícito de `scope --language`; o `transform` aceita só `-l`/`--language`

### Estatísticas
- 542 testes passando em 47 suites de integração, 0 falhas
- 11 GAP-2026 fechados
- 3 targets de cross-compile Windows verdes
- 19 ADRs em `docs/decisions/` (0019-0037)

### Migração `--lang` para `--locale`

```bash
# Descobrir todos os arquivos com --lang
rg -l -- '--lang\b' .

# Substituir em massa preservando outros matches
fd -e sh -e md -e toml -e yml -e yaml -e json -x sd -- '--lang\b' '--locale' {}

# Ou via ruplacer (só simula até você acrescentar --go)
ruplacer -- '--lang' '--locale' .
```


## Toda flag local, por comando
- Esta lista nomeia toda flag local de cada comando, e as flags globais ficam em `atomwrite --help`.
- `agent-surface`: `--format`
- `apply`: `--ack-overwrite`, `--all`, `--allow-shrink`, `--backup`, `--dry-run`, `--format`, `--keep-backup`, `--no-backup`, `--no-syntax-check`, `--retention`, `--show`, `--strict-atomic`
- `backup`: `--dry-run`, `--output-dir`, `--retention`
- `batch`: `--backup`, `--batch-size`, `--dry-run`, `--file`, `--input-schema`, `--keep-backup`, `--no-backup`, `--retention`, `--strict-atomic`, `--transaction`
- `calc`: `--stdin`
- `case`: `--backup`, `--dry-run`, `--exclude`, `--include`, `--keep-backup`, `--no-backup`, `--preserve-timestamps`, `--retention`, `--strict-atomic`, `--subvert`, `--to`
- `codemod`: `--backup`, `--dry-run`, `--keep-backup`, `--no-backup`, `--retention`, `--rules`, `--strict-atomic`
- `commands`: `--brief`, `--include-globals`
- `completions`: `--install`
- `config`: não tem flag local própria, só o conjunto global
- `copy`: `--backup`, `--dry-run`, `--force`, `--keep-backup`, `--no-backup`, `--no-reflink`, `--preserve-xattr`, `--preserve`, `--recursive`, `--retention`, `--strict-atomic`
- `count`: `--by-extension`, `--by-size`, `--exclude`, `--include`, `--top`
- `del`: `--backup`, `--dry-run`, `--force-missing`, `--keep-backup`, `--no-backup`, `--preserve-timestamps`, `--retention`, `--strict-atomic`
- `delete`: `--backup`, `--confirm`, `--dry-run`, `--exclude`, `--include`, `--keep-backup`, `--no-backup`, `--older-than`, `--plan`, `--recursive`, `--retention`, `--yes`
- `diff`: `--algorithm`, `--context`, `--stat`, `--unified`
- `doctor`: `--strict`
- `edit`: `--after-line`, `--after-match`, `--allow-sequential-drift`, `--backup`, `--before-line`, `--before-match`, `--between`, `--delete-range`, `--dry-run`, `--expect-checksum`, `--fuzzy-threshold`, `--fuzzy`, `--keep-backup`, `--line-ending`, `--multi`, `--new-file`, `--new`, `--no-backup`, `--old-file`, `--old`, `--partial`, `--preserve-timestamps`, `--range`, `--replace-all`, `--retention`, `--strict-atomic`, `--wal-policy`
- `edit-loop`: `--allow-sequential-drift`, `--backup`, `--keep-backup`, `--line-ending`, `--no-backup`, `--partial`, `--retention`, `--strict-atomic`, `--syntax-check`
- `extract`: `--delimiter`, `--field`, `--stdin`
- `extract-artifact`: `--max-sample-rows`, `--max-text-chars`
- `get`: não tem flag local própria, só o conjunto global
- `hash`: `--exclude`, `--recursive`, `--stdin`, `--verify`
- `list`: `--all`, `--count-by-ext`, `--depth`, `--exclude`, `--format`, `--include`, `--long`
- `locale`: `--clear`, `--set`
- `move`: `--backup`, `--dry-run`, `--force`, `--keep-backup`, `--no-backup`, `--preserve-hardlinks`, `--retention`
- `outline`: `--format`, `--kind`, `--language`, `--positions`
- `prune-backups`: `--dry-run`, `--max-age-secs`, `--max-count`, `--recursive`
- `query`: `--kinds`, `--language`, `--positions`, `--query`, `--tree`
- `read`: `--all`, `--bytes`, `--context`, `--crop`, `--dpi`, `--enclosing`, `--force`, `--format`, `--grep`, `--head`, `--high-res`, `--line`, `--lines`, `--max-tokens`, `--no-comments`, `--number`, `--ocr`, `--page`, `--pages`, `--stat`, `--symbol`, `--tail`, `--verify-checksum`
- `recipe`: não tem flag local própria, só o conjunto global
- `regex`: `--case-insensitive`, `--digits`, `--no-anchors`, `--repetitions`, `--spaces`, `--stdin`, `--words`
- `replace`: `--backup`, `--dry-run`, `--exclude`, `--expect-checksum`, `--fuzzy-threshold`, `--fuzzy`, `--include`, `--keep-backup`, `--literal`, `--max-replacements`, `--no-backup`, `--preserve-case`, `--preserve-timestamps`, `--preview`, `--progress-every`, `--regex`, `--retention`, `--strict-atomic`, `--word`
- `rollback`: `--backup`, `--dry-run`, `--keep-backup`, `--latest`, `--no-backup`, `--retention`, `--strict-atomic`, `--timestamp`, `--verify`
- `scope`: `--action`, `--backup`, `--delete`, `--dry-run`, `--exclude`, `--include`, `--keep-backup`, `--language`, `--no-backup`, `--pattern`, `--query`, `--replace-with`, `--retention`, `--strict-atomic`
- `scratch`: não tem flag local própria, só o conjunto global
- `search`: `--binary`, `--case-insensitive`, `--context`, `--count`, `--exclude`, `--files`, `--fixed`, `--format`, `--include-fifo`, `--include`, `--invert`, `--limit`, `--max-columns`, `--max-count`, `--max-filesize`, `--multiline`, `--no-begin-end`, `--offset`, `--pcre2`, `--regex`, `--smart-case`, `--sort`, `--target`, `--word`
- `semantic-merge`: `--backup`, `--base`, `--dry-run`, `--expect-checksum`, `--fail-on-conflict`, `--keep-backup`, `--no-backup`, `--no-conflict-markers`, `--ours`, `--output`, `--retention`, `--strict-atomic`, `--theirs`, `--write-conflict-markers`
- `semantic-search`: `--include-backups`, `--index-dir`, `--k`, `--min-score`
- `set`: `--backup`, `--dry-run`, `--keep-backup`, `--no-backup`, `--preserve-timestamps`, `--retention`, `--strict-atomic`
- `sparse`: não tem flag local própria, só o conjunto global
- `stat`: `--all`, `--bytes`, `--context`, `--crop`, `--dpi`, `--enclosing`, `--force`, `--format`, `--grep`, `--head`, `--high-res`, `--line`, `--lines`, `--max-tokens`, `--no-comments`, `--number`, `--ocr`, `--page`, `--pages`, `--stat`, `--symbol`, `--tail`, `--verify-checksum`
- `transform`: `--backup`, `--dry-run`, `--exclude`, `--include`, `--inline-rules`, `--keep-backup`, `--language`, `--no-backup`, `--pattern`, `--retention`, `--rewrite`, `--rules`, `--strict-atomic`, `--verify-parse`
- `verify`: não tem flag local própria, só o conjunto global
- `wal-heal`: `--dry-run`, `--max-duration-ms`, `--threshold-secs`
- `wal-stats`: `--dry-run`
- `watch`: `--checksum`, `--debounce-ms`, `--gitignore`, `--idle-exit-ms`, `--max-events`
- `write`: `--ack-overwrite`, `--allow-empty-stdin`, `--allow-shrink`, `--append`, `--auto-rotate`, `--backup`, `--confirm`, `--dry-run`, `--durability`, `--expect-checksum`, `--keep-backup`, `--line-ending`, `--max-size`, `--no-backup`, `--no-checksum-when-empty`, `--prepend`, `--preserve-timestamps`, `--require-backup`, `--require-large-ack`, `--retention`, `--risk-threshold`, `--strict-atomic`, `--syntax-check`, `--wal-policy`