O diretório de trabalho
Cada artefato que o pipeline produz, onde ele vive, o que o valida e o que é seguro apagar.
O diretório de trabalho (--work) é onde vive tudo o que o pipeline produz. Um por
repositório que você está documentando.
<work>/
phase1/
graph.json the call graph — everything downstream reads this
functions.csv every function, flat, for grepping or a spreadsheet
graph.dot Graphviz: dot -Tsvg graph.dot -o graph.svg
dropped-calls.json calls we could NOT resolve, categorized — not hidden
scan-coverage.json files we could NOT read or fully parse — not counted as covered
phase2/
cards/<rel>.json one card per source file, mirroring the source tree
cards/_coverage.json how many files got prose, and which did not
cards/_rejected/ replies that produced no usable card (capped at 20)
skeleton.yaml the stage spine
assignment.json file → stage
organization.yaml intra-stage groups + reading order
strategy.json which strategy produced the above
phase3/
narration.json stage and system prose
registers.json cross-stage state registers
cache/ content-hash caches for prose and registers
handbook/ the rendered output, once you run `render`
run-manifest.json model, phases, timings and token usage of the last good runTrês propriedades nas quais vale a pena se apoiar
Tudo é validado por schema na leitura
Cada artefato carrega um campo version e é validado com zod ao ser carregado. Um
artefato corrompido ou editado à mão falha ruidosamente e se identifica:
handbook: error: work/myrepo/phase2/skeleton.yaml: stages.3.id: InvalidEle nunca se propaga para uma fase posterior.
Duas exceções deliberadas, ambas por resiliência, não por frouxidão:
- Fichas — um único arquivo de ficha que não parseia é pulado, não fatal. Do contrário, um JSON alheio ou meio sincronizado no diretório de fichas quebraria o resume, as Phases 2b/2c/3 e todo carregamento do modelo.
- Metadados
languagesemgraph.json— opcionais, porque não existe aqui um mecanismo de migração de artefatos, e todo grafo escrito antes de existirem as declarações de fidelidade precisa continuar validando. OunparsedFilesé opcional pela mesma razão: ausente significa que a análise é anterior a esse registro, não que nada falhou.
Toda escrita é atômica
Escreve-se em um arquivo temporário e depois renomeia-se. Um travamento no meio da escrita nunca deixa um artefato pela metade para a próxima execução engasgar.
Uma execução por diretório de trabalho
generate e resync adquirem o mesmo lock reentrante de diretório. Uma execução
concorrente da CLI e um job do Studio sobre os mesmos artefatos intercalariam escritas; o
segundo é recusado com uma mensagem clara.
O que é seguro fazer
| Ação | Seguro? | Observação |
|---|---|---|
| Apagar o diretório de trabalho inteiro | ✅ | Nada fora dele foi modificado. Regenere do zero. |
| Commitá-lo no git | ✅ | É tudo texto. Útil para revisar o que uma regeneração mudou. |
Apagar phase3/cache/ | ✅ | Custa uma renarração completa na próxima execução. |
Apagar phase2/cards/_rejected/ | ✅ | Apenas diagnóstico. Limpo automaticamente no início de uma nova passada de fichas. |
Editar skeleton.yaml à mão | ✅ | Ele é validado na leitura, e --skeleton existe precisamente para isso. |
Editar graph.json à mão | ⚠️ | Ele é gerado. Em vez disso, rode analyze de novo. |
Apagar phase2/strategy.json | ⚠️ | A próxima execução cai para file, o que pode não corresponder aos artefatos. |
| Compartilhá-lo publicamente | ⚠️ | As fichas citam e descrevem o seu código-fonte. Trate-o como código-fonte. |
Lendo-o à mão
O grafo é o mais interessante:
# how big is this codebase, really
jq '.metadata | {files: (.scannedFiles|length), nInternalFunctions, nEdges}' phase1/graph.json
# the busiest functions — where a change is most likely to fan out
jq -r '.nodes | to_entries | map(select(.value.kind=="internal"))
| sort_by(-.value.nCallers) | .[:15]
| .[] | "\(.value.nCallers)\t\(.value.qualname)\t\(.value.file)"' phase1/graph.json
# what could not be resolved, by category
jq '.metadata.byCategory' phase1/dropped-calls.json
# which files the scan could not turn into facts, and why
jq '.metadata.byReason, .files' phase1/scan-coverage.json
# which files never got prose
jq '.missing' phase2/cards/_coverage.jsonO functions.csv está lá pela mesma razão — às vezes a ferramenta mais rápida é uma
planilha.
Os dois arquivos de cobertura respondem a perguntas diferentes
O _coverage.json responde "quais arquivos o modelo não conseguiu descrever?". O
scan-coverage.json responde à pergunta que está por baixo dessa: "quais arquivos o
parser sequer chegou a conseguir ler?" — com um reason de unreadable, unparsable
ou partial.
Os dois primeiros não contribuem com nenhum fato, então também saem do scannedFiles
do graph.json: nada a jusante escreve uma ficha sobre um arquivo que ninguém abriu para
em seguida contá-lo como descrito. Os arquivos partial ficam — o tree-sitter se
recuperou do erro de sintaxe, e as funções que ele encontrou são reais, apenas
incompletas.
Um array files vazio significa que todo arquivo varrido parseou sem problemas. Isso é
uma afirmação; o artefato simplesmente não existir não é.
Onde mais as coisas são escritas
O Handbooks escreve fora do diretório de trabalho em exatamente dois lugares, ambos ativados pelo comando que você executou:
<source>/.handbook-patches/— criado peloapply, guarda os backups e seus manifestos. Um.gitignoreé escrito nele automaticamente, para que backups nunca entrem no git.$HOME/.handbook-studio/— o registro de repositórios do Studio e seus diretórios de trabalho criados automaticamente. Mova-o com--state-dir.
As cinco fases
O que cada fase de geração faz, quanto ela custa, para o que degrada quando falha e como reexecutar só uma delas.
Fidelidade da análise
Dois níveis de análise produzem saídas de aparência idêntica. Isso é uma armadilha, então cada adaptador declara o que consegue entregar e o handbook divulga isso.