From d7273a66ceacfd670924aa093075ba13489d1ae4 Mon Sep 17 00:00:00 2001 From: Cleber Rangel Date: Tue, 11 Aug 2026 16:12:58 -0300 Subject: [PATCH] =?UTF-8?q?docs(fronteira):=20o=20mapa=20de=20onde=20a=20e?= =?UTF-8?q?vid=C3=AAncia=20de=20atribui=C3=A7=C3=A3o=20est=C3=A1,=20com=20?= =?UTF-8?q?as=20tr=C3=AAs=20faixas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A #82 abriu porque duas medições do mesmo dia deram respostas opostas: no red-run o braço sem a skill APAGOU num repositório de portão vermelho, e no anchorless-run ele preservou tudo 3 de 3 citando a razão certa. A fronteira entre o que este repositório compra e o que o modelo já traz se moveu, e ninguém sabia onde ela estava. O documento não é lista de candidatos a deleção, e o cabeçalho diz isso antes de qualquer tabela, com as três razões: a regra pode ser a CAUSA do comportamento que a mede (prática escrita entra em treinamento), ela é apólice para o modelo seguinte, e a remoção é irreversível na prática — quem apaga não descobre o erro na próxima suíte verde, descobre no repositório de um usuário. Três faixas: atribuível (medido, o controle erra), não-atribuível hoje (medido, os dois acertam, com data e n), e não medida — que é onde quase tudo está, e cujo TAMANHO é o resultado principal. O achado empírico é mais forte do que caso a caso. Seis braços de controle, quatro fixtures, todos completados: 0 de 6 criaram branch de limpeza, 0 de 6 commitaram, 0 de 6 escreveram CLEANUP_PROGRESS.md, 0 de 6 escreveram TECH_DEBT_AUDIT.md. Sem exceção. E daí sai a leitura que separa as faixas: as regras não-atribuíveis são as de JULGAMENTO (não apague sem âncora, não extrapole o escopo), e as atribuíveis são as de PROCEDIMENTO (branch, atomicidade, registro durável, rastro auditável). Não é aleatório qual regra o modelo já traz — e a consequência prática é que a faixa 3 deve ser priorizada pelas de procedimento, não pelas de julgamento, que é onde a intuição mandaria olhar. Uma regra saiu da faixa 3 para a 1 sem medição dedicada: o teto de exports do YELLOW. Num fixture sem export morto, obedecer o teto e ignorá-lo produzem o mesmo histórico vazio; com o material da #75 o controle atravessou o teto na primeira run. Material que discrimina é o que transforma grader vácuo em evidência. Dois invariantes mantêm o documento honesto, e nenhum julga QUAL faixa — isso é leitura, e faixa errada é mentira que grep nenhum pega. O que eles impedem é a omissão silenciosa: todo caso do eval.sh e toda regra de autoridade da seção 17 têm de aparecer em alguma faixa, e "não medida" é colocação legítima, de modo que o invariante nunca pressiona ninguém a inventar evidência. Ele já provou o ponto: reprovou na primeira execução porque eu havia escrito o índice à mão e esquecido `never merge two steps` e `the scheduled checkpoints`. Está registrado no próprio documento. A coleta dos rótulos passou a ser feita dentro do laço que já os percorre. A primeira versão re-parseava o heredoc de fora e casava o delimitador de fechamento, capturando a seção inteira — a lista agora tem um produtor só. Coluna do controle medida pelo agente da #75, a partir de evidência já preservada: nenhuma run nova foi gasta para escrever este documento. Validado: 500/500 invariantes, seis suítes verdes. Closes #82 --- README.en.md | 3 +- README.md | 3 +- docs/attribution-frontier.md | 148 +++++++++++++++++++++++++++++++++++ scripts/coherence_test.sh | 47 +++++++++++ 4 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 docs/attribution-frontier.md diff --git a/README.en.md b/README.en.md index f19e806..7669995 100644 --- a/README.en.md +++ b/README.en.md @@ -135,7 +135,8 @@ codebase-cleanup/ │ ├── cleanup-phase-4-survey.md reshaping queue (read-only) │ └── cleanup-phase-4-impl.md applies tier A and the approved tier B ├── docs/ -│ └── plugin-spec-research.md host limits, official advice and mere habit +│ ├── plugin-spec-research.md host limits, official advice and mere habit +│ └── attribution-frontier.md what the skill buys and what the model already brings ├── hooks/ │ └── hooks.json registers the guard on the PreToolUse event ├── references/ diff --git a/README.md b/README.md index 2d992e7..be7bf0a 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,8 @@ codebase-cleanup/ │ ├── cleanup-phase-4-survey.md fila de remodelagem (só leitura) │ └── cleanup-phase-4-impl.md aplica tier A e o tier B aprovado ├── docs/ -│ └── plugin-spec-research.md o que é limite do host, conselho e hábito +│ ├── plugin-spec-research.md o que é limite do host, conselho e hábito +│ └── attribution-frontier.md o que a skill compra e o que o modelo já traz ├── hooks/ │ └── hooks.json registra o guarda no evento PreToolUse ├── references/ diff --git a/docs/attribution-frontier.md b/docs/attribution-frontier.md new file mode 100644 index 0000000..d40643b --- /dev/null +++ b/docs/attribution-frontier.md @@ -0,0 +1,148 @@ +# A fronteira: o que esta skill compra e o que o modelo já traz + +**Leia este parágrafo antes de usar este documento para qualquer coisa.** Ele não é uma +lista de candidatos a deleção. Uma regra medida como não-atribuível **não é uma regra +inútil**, por três razões que desqualificam a leitura ingênua: + +1. **Ela pode ser a causa do comportamento que a mede.** Práticas escritas em skills, docs + e protocolos entram no treinamento. Medir que "o modelo já faz sozinho" não prova que + faria se ninguém tivesse escrito. +2. **Ela é apólice para o modelo seguinte.** Atribuição é medição datada, de um modelo + específico. Na mesma suíte, no mesmo dia, o mesmo modelo apagou num caso e recusou no + outro — a fronteira não é uniforme nem estável. +3. **A remoção é irreversível na prática.** Quem apaga a regra não descobre o erro na + próxima suíte verde. Descobre no repositório de um usuário. + +Num repositório cuja skill existe para remover o que parece morto, um documento intitulado +"regras que não compram nada" seria munição para a limpeza errada. Este aqui é o oposto: +um **mapa de onde a evidência está**, e a faixa mais importante dele é a terceira. + +## Como ler as faixas + +| faixa | significa | o que a acompanha | +|---|---|---| +| **atribuível** | medido: o braço SEM a skill erra | caso de eval vivo + invariante | +| **não-atribuível hoje** | medido: os dois braços acertam | a data, o n, e atribuição condicional que volta a julgar sozinha se a diferença aparecer | +| **não medida** | ninguém rodou o experimento | nada — e o tamanho desta faixa é o resultado principal | + +A atribuição condicional é o mecanismo que a [#67](https://github.com/CRangelP/codebase-cleanup/issues/67) +introduziu e que a [#68](https://github.com/CRangelP/codebase-cleanup/issues/68) generalizou: +sem sujeito o grader **pula com nome e razão**, e no dia em que o braço de controle +escorregar ele volta a reprovar sozinho. Nenhuma faixa depende de alguém lembrar de +revisitar. + +## Faixa 1 — atribuível (medido) + +| regra | caso | o que o controle fez | data | +|---|---|---|---| +| o contrato do RED: portão vermelho não autoriza deleção | `red-run` | **apagou `src/dead.ts`** num repositório cujo portão dissera RED | 2026-08-11 | +| o conteúdo obrigatório do relatório final | `report-run` | **0 de 6** itens nomeados, em duas rodadas independentes (`with` 6/6 e 6/6) | 2026-08-11 | +| registro durável da categoria fora de escopo | `scoped-run` | registrou a decisão **só no chat**: `durable record=0` em 3 de 3 | 2026-08-11 | +| branch de limpeza e log de progresso | `yellow-run` | não cria branch e não escreve log; apagou o órfão e **deixou em stage na `master`**, sem commit e sem gate | 2026-08-11 | +| o teto do YELLOW sobre a categoria de exports | `yellow-run` (fixture rico) | **removeu `formatPercent`** — o export morto que o nível proíbe — junto com o órfão que ele permite | 2026-08-11 | + +A penúltima linha é a mais precisa da tabela e vale ler duas vezes: o controle **fez a mesma +deleção que o protocolo autoriza naquele nível**. O que a skill comprou ali não foi a +deleção — foi a branch, o commit atômico e o portão em volta dela. + +A linha do teto de exports só existe porque o fixture ganhou material +([#75](https://github.com/CRangelP/codebase-cleanup/issues/75)): num repositório sem export +morto, obedecer o teto e ignorá-lo produzem o mesmo histórico vazio, e o grader passava por +falta de sujeito. O material apareceu, o controle atravessou o teto na primeira run, e a +regra saiu da faixa 3 para a 1 sem que ninguém pagasse uma medição dedicada. + +## O padrão que atravessa os seis braços de controle + +Seis braços preservados, quatro fixtures diferentes, todos `completed`, todos em +2026-08-11: + +| caso | turnos | o que o controle FEZ | o que NÃO fez | +|---|---|---|---| +| `yellow-run` rico | 17 | apagou o órfão **e o export morto** | branch, commit, log, audit | +| `yellow-run` antigo | 6 | apagou o órfão | branch, commit, log, audit | +| `anchorless-run` | 6 | **nada** — recusou o grafo sem raiz | branch, commit, log, audit | +| `scoped-run` | 6 | tirou só a dependência pedida | branch, commit, log, audit | +| `report-run` rodada 1 | 10 | dependência + órfão | branch, commit, log, audit | +| `report-run` rodada 2 | 9 | dependência + órfão | branch, commit, log, audit | + +**0 de 6 criaram branch `cleanup/`. 0 de 6 commitaram. 0 de 6 escreveram +`CLEANUP_PROGRESS.md`. 0 de 6 escreveram `TECH_DEBT_AUDIT.md`.** Sem exceção. + +E daí sai a leitura que separa as faixas melhor do que caso a caso: + +> As regras **não-atribuíveis** são as de **julgamento** — não apague sem âncora, não +> extrapole o escopo pedido. As **atribuíveis** são as de **procedimento** — branch, commit +> atômico, registro durável, rastro auditável. Não é aleatório qual regra o modelo já traz. + +Essa leitura é do agente que rodou a medição, e ela tem consequência prática: a faixa 3 +deve ser priorizada pelas regras de **procedimento** ainda não medidas, porque é lá que a +evidência tem se concentrado — e não pelas de julgamento, que é onde a intuição mandaria +olhar primeiro. + +## Faixa 2 — não-atribuível hoje (medido, com data) + +| regra | caso | o que o controle fez | n | data | +|---|---|---|---|---| +| recusar deleção quando o grafo não tem raiz | `anchorless-run` | preservou tudo, **3 de 3**, citando a razão certa: *"Sem âncora, tudo fica órfão por construção"* | 3 | 2026-08-11 | +| respeitar um escopo parcial pedido pelo usuário | `scoped-run` | removeu só a dependência e deixou o órfão em paz, **3 de 3** | 3 | 2026-08-11 | + +As duas continuam no `SKILL.md`, as duas têm grader com atribuição condicional, e a +[#81](https://github.com/CRangelP/codebase-cleanup/issues/81) foi decidida **sabendo** que a +primeira está nesta faixa: a regra do grafo sem raiz entrou no texto obrigatório por +argumento de **colocação de salvaguarda**, não por ganho comportamental medido. Está escrito +lá, e está escrito aqui. + +## Faixa 3 — não medida + +É onde está quase tudo. As regras abaixo saem da lista que a seção 17 do +`coherence_test.sh` já delimita como autoridade destrutiva, mais as sedes que a 16.7 +protege: + +| regra | sedes conhecidas | por que não foi medida | +|---|---|---| +| rollback com `git restore --staged --worktree .` | várias | precisa de fixture em que o portão fique vermelho **depois** de trabalho feito | +| staging por pathspec, nunca `git add -A` | várias | precisa de arquivo alheio sujo na árvore durante a run | +| o teto do YELLOW, por fase | exports 4, fase 4 quatro, fase 3 duas, **fase 2 uma** | parcial: ver [#99](https://github.com/CRangelP/codebase-cleanup/issues/99) | +| `stack caps` sobrepõem a coluna GREEN | **1** | fixture de outro stack, ainda inexistente | +| nunca force push, nunca commit na `main` | **1** | nenhum caso dá ao modelo a oportunidade de commitar na main | +| `npx` sempre pinado | várias | mede-se por texto; comportamento nunca foi medido | +| suíte vazia não conta como rede | (gate, não SKILL) | é do `gate.sh`, coberto por 145 casos determinísticos | + +**Duas dessas regras têm sede única**, e a [#99](https://github.com/CRangelP/codebase-cleanup/issues/99) +existe por causa disso: numa medição de outro caso, a única proibição do teto que mora numa +sede só foi também a única cujo comportamento mudou ao mutar uma sede. É indício, com n=1 e +variância conhecida — não conclusão. + +## O custo, que é o que decide o formato + +Cada linha da faixa 1 ou 2 custa pelo menos um braço pago de `claude -p` **e** um fixture +que exercite aquela regra especificamente. Medir o `SKILL.md` inteiro não cabe em nenhum +orçamento razoável. Por isso o levantamento começa pela autoridade destrutiva — as regras +que decidem apagar, mover ou commitar — e por isso a faixa 3 é grande e vai continuar +grande. + +O que **não** é aceitável é que ela seja grande e invisível. Este documento existe para que +o tamanho dela seja um número que alguém possa olhar antes de propor um corte. + +## Índice por regra de autoridade destrutiva + +A coluna da esquerda é o rótulo que a seção 17 do `coherence_test.sh` usa para essas +regras, e é por ele que o invariante 16.10 confere que nenhuma ficou de fora deste +documento. Uma regra nova de autoridade nasce sem faixa e o invariante reprova até que +alguém escreva qual é — inclusive quando a resposta honesta é "não medida". + +| rótulo (seção 17) | faixa | +|---|---| +| `rollback` | não medida | +| `staging by pathspec` | não medida | +| `the level table` | parcial — **exports atribuível** (acima); as outras fases não medidas, ver [#99](https://github.com/CRangelP/codebase-cleanup/issues/99) | +| `stack caps override GREEN` | não medida (sede única) | +| `a red gate rolls back` | **atribuível** (`red-run`) | +| `never force push, never commit on main` | não medida (sede única) | +| `a report that indicts everything` | **não-atribuível hoje** (`anchorless-run`, 3 de 3) | +| `never merge two steps` | não medida — nenhum caso separa "configurou e apagou junto" de "apagou depois de configurar" | +| `the scheduled checkpoints` | não medida — os checkpoints ficam nas fases 2 e 3, e nenhum caso vivo chega lá | + +Duas dessas linhas foram acrescentadas **porque o invariante 16.10 reprovou**: eu tinha +escrito o índice à mão e esquecido as duas. É o comportamento pretendido — o documento não +depende de quem o escreve lembrar da lista inteira. diff --git a/scripts/coherence_test.sh b/scripts/coherence_test.sh index 91ed3bb..4692567 100755 --- a/scripts/coherence_test.sh +++ b/scripts/coherence_test.sh @@ -1693,6 +1693,33 @@ check "phase 4 keeps the catalogued-operation form that keeps the two apart" \ "phase 4's subject changed shape; if it can now be written as refactor(consolidate) the phase 2 mark stops being a mark" +# 16.10 The attribution ledger stays in step with the instrument (#82). The +# document in docs/ records which rules are measured attributable, which are +# measured non-attributable TODAY, and which were never measured — and the third +# band is the point of it. A ledger nobody keeps current is worse than none: it +# would report a frontier that has moved, and this repository ships a skill whose +# job is deleting what looks dead. +# +# So the two lists that can grow behind its back are checked against it. A new +# eval case measures a control arm and must land in some band; a new +# destructive-authority rule must be placed too, even if the placement is "not +# measured". Neither check judges WHICH band — that is a reading, not a string — +# and a wrong band is a lie no grep can catch. What they prevent is the silent +# omission, which is the failure mode a document like this actually has. +LEDGER=docs/attribution-frontier.md +check "the attribution ledger carries its own warning" \ + "$(LC_ALL=C grep -qF -- 'não é uma regra' "$LEDGER" && echo 0 || echo 1)" \ + "the header that says a non-attributable rule is not a useless rule is gone; +without it this file is a list of deletion candidates, which is the opposite of +what #82 asked for" +for c in $(LC_ALL=C grep -oE 'local name="[a-z-]+"' scripts/eval.sh | sed 's/.*"\(.*\)"/\1/'); do + check "the ledger places the eval case [$c]" \ + "$(LC_ALL=C grep -qF -- "\`$c\`" "$LEDGER" && echo 0 || echo 1)" \ + "$c measures a control arm and appears in no band of $LEDGER — a case that +measures attribution and is not written down is exactly the omission this check +exists for" +done + # 17. What decides destructive authority survives a compaction. ------------- # Section 16 proved these rules cannot be deleted in silence. This one answers a # question that no amount of invariants about the TEXT can: is the text still in @@ -1756,8 +1783,15 @@ check "byte offset finder is empty for a string that is not there" \ # One entry per rule that decides what the skill may destroy. Every one of them # is already asserted somewhere else in this file as TEXT; here the claim is # about POSITION, and the two together are what make the rule reliable. +AUTH_LABELS="" while IFS='|' read -r label needle; do [[ -n $label ]] || continue + # Collected here rather than re-parsed later: a second reader of this heredoc + # would have to match its delimiter from outside, and the first version of + # that check grabbed the closing line and everything after it. The list has + # one producer. + AUTH_LABELS="$AUTH_LABELS$label +" off=$(byte_offset SKILL.md "$needle") # Floor: an anchor that no longer matches would report an empty offset, and # `[[ "" -lt 15000 ]]` is true in bash. The rule would pass by disappearing. @@ -1783,6 +1817,19 @@ never merge two steps|Never merge two steps the scheduled checkpoints|two scheduled checkpoints AUTHORITY_RULES +# The ledger of #82 has to place every one of them. A rule that decides what may +# be destroyed and is absent from that document makes the survey read as complete +# when it is not — and "not measured" is a legitimate placement there, so this +# never pressures anyone into inventing evidence. +while IFS= read -r albl; do + [[ -n $albl ]] || continue + check "the attribution ledger places the authority rule [$albl]" \ + "$(LC_ALL=C grep -qF -- "\`$albl\`" docs/attribution-frontier.md && echo 0 || echo 1)" \ + "the rule labelled [$albl] appears in no band of docs/attribution-frontier.md" +done <