---
name: doc-touch
description: Atualização INCREMENTAL da documentação project-doc — mapeia o diff do trabalho recente pros docs afetados (via scope inverso) e re-projeta SÓ eles, sem re-mineração completa. Use quando Pedro diz "/doc-touch", "atualiza a doc do que eu mexi", "toca a doc", "atualiza a documentação disso que fizemos", ou após um ciclo de código quando a doc dos arquivos tocados precisa acompanhar. NÃO substitui o /project-doc FULL (mineração completa) — é o complemento frequente entre FULLs.
---

# doc-touch — atualização incremental da doc

Irmã do `/project-doc` (mesmo plugin, mesma estrutura de doc, mesmos invariantes). O FULL minera tudo e re-projeta tudo; o **touch** atualiza só os docs cujo `scope:` intersecta o diff do trabalho recente. Mexeu → tocou a doc. O FULL vira evento raro.

## Fluxo (5 passos)

### 1 · Grafo fresco + plano determinístico
```bash
graphify update "<root>" --force    # AST, ZERO LLM, segundos, idempotente. Ausente → cria; fresco → no-op.
python3 plugins/project-doc/lib/pattern_check.py --project-root "<root>" --touch-plan --json
```
O touch **documenta** ⇒ é modo PESADO na regra do grafo (canônica: `skills/project-doc/SKILL.md` → Workflow Engine → Passo 0): doc nova nunca sai de grafo velho. Rode sem anunciar custo nem pedir confirmação — só informe o status. `graphify` não instalado ⇒ **avise e siga** (a re-projeção vem do diff, não do mapa; o touch não consome `graph_map`).
Devolve `{changed, docs:{doc:{files, already_current}}, pending_docs, seam_review, unscoped_new, dead_scope, ledger_last_commit}`. O `changed` = working tree ∪ staged ∪ `ledger.last_commit..HEAD` (mesma janela do backward-delta do journal, **lida read-only**).

**Trabalhe sobre `pending_docs`, não sobre `docs`.** `docs` lista tudo que o diff toca; `pending_docs` exclui os que já absorveram a mudança (doc mais novo que os arquivos). Sem isso o touch repetido vira no-op — enquanto o trabalho não é commitado, o `git diff` segue mostrando os mesmos arquivos. `pending_docs` vazio → reporte "nada a tocar" e pare.

### 2 · Re-projeção escopada, por doc
Para cada doc do plano (sequencial se ≤3; subagentes paralelos se mais): o agente recebe **o doc atual + SÓ os arquivos mudados do scope + o diff deles** (`git diff <ledger_last_commit> -- <files>` + working tree) e:
- Atualiza **apenas as seções afetadas pelo diff**; preserva o resto intocado (não reescreve, não "melhora").
- Obedece as **Regras de escrita assertiva** do SKILL grande (`skills/project-doc/SKILL.md` → Rules): nome/número/lista só por derivação mecânica no run; ponteiro = arquivo+símbolo; "ativa" exige evidência de wiring; costura citada existe nos dois lados.
- Fato durável genuinamente NOVO que entrou na doc → anotar para o passo 4.

### 3 · Gate doc-lint (determinístico, antes do re-stamp)
```bash
python3 plugins/project-doc/lib/doc_lint.py --project-root "<root>" --docs <docs tocados> --json
```
FAIL → corrigir com a evidência que o próprio lint dá e re-rodar (máx 2 iterações; persiste → reportar FAIL, não silenciar). Falso-positivo legítimo (var dinâmica, config externa) → `<!-- lint:ignore TOKEN -->` ou `.claude/.project-doc/lint-allow.txt`, com justificativa no report.

### 4 · Re-stamp + journal
Por doc tocado:
- Frontmatter: `generated:` = hoje **e `generated-commit:` = HEAD atual** (é o que evita o doc re-acusar stale no mesmo dia — o staleness passa a comparar por commit).
- `doc-sig:` recomputada — **recompute só DEPOIS do corpo estar final** (o `hash8` é `sha256(corpo)`; qualquer edição posterior deixa a sig mentindo) **e preserve a gen do doc-set**:
  ```bash
  GEN=$(sed -n 's/.*project-doc:v2 gen=\([0-9.]*\).*/\1/p' <root>/.claude/CLAUDE.md | head -1)
  python3 plugins/project-doc/lib/pattern_check.py --sig <doc> | sed "s/@gen=[0-9.]*#/@gen=$GEN#/"
  ```
  ⚠️ **`--sig` carimba sempre o `CURRENT_GEN` do CÓDIGO**, não a gen do doc. Quando o código está à frente do doc-set (ex.: código 3.7, docs 3.6), a saída crua **bumpa a gen** — violando o invariante "Gen NÃO bumpa" logo abaixo. A gen só muda em FULL.
- Journal (disciplina do FULL, nunca relaxar): `journal.py adopt` **só** de fato durável genuinamente novo (nunca adopt do que já está vivo); `journal.py invalidate` **só** contradição frontal com evidência arquivo:linha.
- **PROIBIDO rodar `journal.py update`** — avançaria `ledger.last_commit` e queimaria o backward-delta do próximo FULL. O ledger pertence ao FULL; o touch é read-only nele.

### 5 · Report + commit
Report curto: docs tocados (com o quê) · `seam_review` (costuras tocadas — verificar se o claim do OUTRO módulo mudou; endereçar no doc de costura da raiz se sim) · `unscoped_new` (arquivos novos em dirs cobertos — oferecer adicionar ao `scope:` do doc certo) · `dead_scope` (renames — corrigir as entradas) · **idade do último FULL** (>30 dias → sugerir `/project-doc`; o touch preserva o resto do doc, inclusive erro pré-existente — é complemento, não substituto).
Commit escopado se Pedro quiser (regras do FULL: só artefatos de doc — docs tocados, `findings.jsonl`, **`graphify-out/` se existir** (o `.gitignore` do projeto é quem exclui `cache/` e os paths de máquina — o `graph.json` é versionado) —, **nunca `git add -A`**, push seguro sem `--force`).

## Invariantes (não-negociáveis)

- **Doc autoral é INTOCÁVEL.** Arquivo com `authored-by: human` no frontmatter (`quality-goals.md`,
  `constraints.md`, `context.md`, `solution-strategy.md`, `glossary.md`, `decisions/*.md` — território
  do `/start-doc`) **nunca é re-projetado, nunca ganha `scope:`, nunca é re-stampado**. Se um aparecer
  no plano, **pule e reporte**. Hoje a proteção é indireta — o `scope: []` vazio o mantém fora do
  `touch-plan` —, mas o passo 5 manda corrigir `dead_scope` e adotar `unscoped_new` no `scope:` do doc
  certo: popular o scope de um autoral quebraria a trava **para sempre e em silêncio**. Antes de
  mexer no `scope:` de qualquer doc, cheque o `authored-by:`. (Achado da revisão de 2026-07-26.)
- **Ativo novo exige linha de durabilidade (gen 3.8).** Se `data-stores.md` está entre os docs
  tocados, confira que todo depósito nele tem bloco correspondente em `durability.md` — é a regra do
  check #24 do FULL, trazida pro touch porque senão um volume novo no compose entra no inventário e
  fica sem cobertura declarada até o próximo FULL (que pode demorar 30 dias). Sem bloco → escreva
  `[TODO: sem cobertura declarada]` e reporte. Silêncio sobre durabilidade é o que a gen 3.8 proíbe.
- **Gen NÃO bumpa.** O touch não invalida docs antigas nem cria campo obrigatório (`generated-commit:` é opcional — ausência não é violação).
- **Grafo em modo PESADO** — o touch escreve doc, então garante o grafo fresco (`graphify update --force`, passo 1), igual ao FULL. O que o touch NÃO faz é *consumir* o mapa: re-projeta do **diff**, sem `graph_map.py`/fan-out. Grafo e doc viajam juntos no commit.
- **Ledger read-only.** Ver passo 4.
- **Nunca re-projetar doc fora do plano.** O touch-plan é o contrato; doc não mapeado não é tocado (nem "aproveitando que estou aqui").
- Secret: as mesmas regras do FULL (nomes SIM, valores NUNCA).

## Output Protocol

```
**Touch 1/5:** grafo {criado | atualizado | já fresco | graphify ausente} · plano → {N} arquivos mudados → {M} doc(s) afetado(s) [+ costuras: {ids}]
**Touch 2/5:** re-projeção → {doc}: {seções atualizadas}
**Touch 3/5:** doc-lint → {ok | X FAILs corrigidos | FAIL persistente: ...}
**Touch 4/5:** re-stamp {M} doc(s) + journal ({adopts} adoções, {invs} invalidações)
**Touch 5/5:** {commitado <hash> | sem commit} · último FULL há {N} dias{ — sugerir /project-doc se >30}
```
