Handbooks
Conceptos

El directorio de trabajo

Cada artefacto que produce el pipeline, dónde vive, qué lo valida y qué es seguro borrar.

El directorio de trabajo (--work) es donde vive todo lo que produce el pipeline. Uno por repositorio que estés 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 run

Tres propiedades en las que apoyarse

Todo se valida contra un esquema al leerse

Cada artefacto lleva un campo version y se valida con zod cuando se carga. Un artefacto corrupto o editado a mano falla de forma ruidosa y se identifica a sí mismo:

handbook: error: work/myrepo/phase2/skeleton.yaml: stages.3.id: Invalid

Nunca se propaga a una fase posterior.

Dos excepciones deliberadas, ambas por resiliencia y no por laxitud:

  • Las fichas — un único archivo de ficha imparseable se omite, no es fatal. Un solo JSON ajeno o a medio sincronizar en el directorio de fichas haría fallar, si no, la reanudación, las phases 2b/2c/3 y cada carga del modelo.
  • Los metadatos languages en graph.json — opcionales, porque aquí no hay mecanismo de migración de artefactos y cada grafo escrito antes de que existieran las declaraciones de fidelidad debe seguir validando. unparsedFiles es opcional por la misma razón: que falte significa que el análisis es anterior a este registro, no que no fallara nada.

Cada escritura es atómica

Se escribe en un archivo temporal y luego se renombra. Un crash a mitad de escritura nunca deja un artefacto a medio escribir con el que se atragante la siguiente ejecución.

Una ejecución por directorio de trabajo

generate y resync toman el mismo lock reentrante de directorio. Una ejecución de la CLI y un job de Studio concurrentes sobre los mismos artefactos intercalarían escrituras; en su lugar, el segundo se rechaza con un mensaje claro.

Qué es seguro hacer

Acción¿Seguro?Nota
Borrar el directorio de trabajo enteroNada fuera de él fue modificado. Regenera desde cero.
Commitearlo a gitEs todo texto. Útil para revisar qué cambió una regeneración.
Borrar phase3/cache/Te cuesta una re-narración completa en la siguiente ejecución.
Borrar phase2/cards/_rejected/Solo diagnósticos. Se limpia automáticamente al empezar una nueva pasada de fichas.
Editar a mano skeleton.yamlSe valida al leerse, y --skeleton existe precisamente para esto.
Editar a mano graph.json⚠️Es generado. Vuelve a ejecutar analyze en su lugar.
Borrar phase2/strategy.json⚠️La siguiente ejecución cae a file, que puede no coincidir con los artefactos.
Compartirlo públicamente⚠️Las fichas citan y describen tu código fuente. Trátalo como código fuente.

Leerlo a mano

El grafo es el interesante:

# 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.json

functions.csv está ahí por la misma razón — a veces la herramienta más rápida es una hoja de cálculo.

Los dos archivos de cobertura responden preguntas distintas

_coverage.json responde a «¿qué archivos no consiguió describir el modelo?». scan-coverage.json responde a la pregunta que hay debajo: «¿qué archivos no llegó siquiera a leer el parser?» — con un reason de unreadable, unparsable o partial.

Los dos primeros no aportan ningún hecho, así que también se quitan de scannedFiles en graph.json: nada aguas abajo escribe una ficha sobre un archivo que nadie abrió para luego contarlo como descrito. Los archivos partial se quedan — tree-sitter se recuperó del error de sintaxis y las funciones que sí encontró son reales, solo que incompletas.

Un array files vacío significa que todos los archivos escaneados parsearon limpiamente. Eso es una afirmación; que el artefacto simplemente no esté, no lo es.

Dónde más se escriben cosas

Handbooks escribe fuera del directorio de trabajo en exactamente dos sitios, ambos opt-in según el comando que ejecutaste:

  • <source>/.handbook-patches/ — creado por apply, guarda las copias de seguridad y sus manifiestos. Se escribe automáticamente un .gitignore dentro para que las copias de seguridad nunca entren en git.
  • $HOME/.handbook-studio/ — el registro de repositorios de Studio y sus directorios de trabajo autocreados. Muévelo con --state-dir.

En esta página