Plugin: manifesto, guardas de protocolo, subagentes por fase e distribuição - #43
Merged
Conversation
… de skill Instalado como plugin, a pasta é copiada para um diretório de cache cujo caminho ninguém adivinha, e um 'scripts/gate.sh' nu seria lido como relativo ao projeto sendo limpo — onde ele não existe. O :-. mantém a instalação como skill pura funcionando. Um invariante novo cobre as sete formas no protocolo; os READMEs ficam de fora porque lá o caminho nomeia o arquivo, não um passo.
A skill proíbe reset --hard, clean, push, commit na main e staging de árvore inteira — em prosa, que é conselho que um modelo pode perder. Instalada como plugin ela passa a barrar os cinco no PreToolUse, antes de rodarem. O guarda só acorda dentro de uma run (branch cleanup/, ou CLEANUP_PROGRESS.md não rastreado) porque hooks de plugin disparam em toda sessão que o habilita, e esses comandos são trabalho normal em qualquer outro lugar. Log rastreado é limpeza mergeada, não run em curso: sem essa distinção o guarda ficaria ativo na main para sempre depois do primeiro merge. Falha aberta em tudo o que for ambíguo. A skill aborta o pipeline quando um comando do protocolo é bloqueado, então um falso positivo derruba uma run legítima — pior que guarda nenhum. Sai 0 ou 2 e nunca 1: o contrato de hook lê 1 como erro não-fatal e deixa o comando passar. 42 casos novos em guard_test.sh, a metade que importa sendo o que ele deixa passar; e invariantes ligando cada comando bloqueado à tabela dos dois READMEs.
Antes a instalação era copiar uma pasta: sem versão, sem canal de atualização, e uma correção de protocolo só chegava a quem copiasse de novo. O marketplace mora no mesmo repo, então /plugin marketplace add aponta para o próprio GitHub e /plugin update passa a existir. A versão é explícita e semver de propósito. Sem ela a chave de cache vira o SHA do commit e todo push vira update; com ela, o usuário só recebe o que foi bumpado — e é por isso que o esquecimento de bump virou invariante, e não disciplina. Copiar a pasta continua funcionando e continua sendo skill; o que muda é de onde vem a atualização. A invocação como plugin leva o namespace (/codebase-cleanup:codebase-cleanup), e os dois READMEs dizem isso.
…vez de descrever O Step 0.2 descrevia o contrato de delegação em prosa e deixava o orquestrador remontá-lo a cada run. Agora são cinco agents em agents/: fases 1 e 1.5 num só, e um par survey/implementação para as fases 2 e 3. A divisão survey/implementação é onde mora o checkpoint, e ela deixa de depender de boa vontade: os dois agents de survey declaram disallowedTools: Write, Edit, então a pergunta chega ao usuário antes de qualquer mudança — um survey que consegue escrever é um survey que consegue decidir, que é exatamente o que o checkpoint existe para impedir. Nenhum deles declara hooks, mcpServers ou permissionMode: plugin agents não suportam esses campos e o Claude Code recusa o agent inteiro. Virou invariante em vez de descoberta na máquina de um usuário. O invariante da árvore dos READMEs também aprendeu agents/: antes ele assumia que todo .md listado morava em references/, e os cinco arquivos novos apareceram como "references/cleanup-phase-2-impl.md não existe no disco". Agora cobre os dois diretórios, nos dois sentidos.
A versão do plugin.json é a chave de cache que decide se uma instalação enxerga atualização. Esquecer o bump falha em silêncio dos dois lados: ninguém recebe erro, a correção só nunca chega. O CHANGELOG dá significado ao número, e um invariante prova que os dois dizem a mesma coisa.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Transforma a skill em plugin de Claude Code, sem deixar de ser skill. Instalar
por cópia continua funcionando; o que muda é que agora ela também é
instalável, versionada, e capaz de trazer garantias que uma skill sozinha não
tem.
Por que
O repo já tinha crescido para além do formato skill: 5 mil linhas de bash, um
contrato de exit codes, três suítes e CI. Estava usando a pasta de skills como
se fosse um pacote.
E, mais importante: as regras mais críticas do protocolo — nunca
git reset --hard, nuncagit clean, nunca push, nunca commit namain,staging por pathspec — viviam como prosa que um modelo pode não seguir. Hooks
de plugin são a única forma de torná-las tão determinísticas quanto
rollback_test.shjá as trata.O que entra
Manifesto (
.claude-plugin/plugin.json). ComSKILL.mdna raiz, a pastacarrega como plugin de skill única; copiada para
~/.claude/skills/, carregacomo plugin de diretório de skills. Nada muda para quem já usa.
Gate por caminho absoluto. Instalado como plugin a pasta vive num
diretório de cache, e
scripts/gate.shseria lido como relativo ao projetosendo limpo — onde não existe. Agora é
"${CLAUDE_PLUGIN_ROOT:-.}/scripts/gate.sh", e o:-.mantém a instalaçãopor cópia intacta.
Os cinco guardas (
hooks/hooks.json→scripts/guard.sh).PreToolUsecom pré-filtro
Bash(git *), bloqueandogit reset --hard,git clean,git push,git commitnamaine staging de árvore inteira.Duas decisões que só apareceram implementando:
cleanup/, ouCLEANUP_PROGRESS.mdnão rastreado. O "não rastreado" não é detalhe —log rastreado numa branch normal é limpeza já mergeada, e sem essa distinção
o guarda ficaria ativo na
mainpara sempre depois do primeiro merge.command: sai 0 ecala. A skill aborta o pipeline quando um comando do protocolo é
bloqueado, então um falso positivo derruba uma run legítima.
Sai 0 ou 2, nunca 1: o contrato de hook lê 1 como erro não-fatal e deixa o
comando passar — seria o guarda falhando em silêncio. Invariante nas duas
suítes.
Cinco subagentes (
agents/), um por fase, com survey e implementaçãoseparados nas fases 2 e 3. Os dois de survey declaram
disallowedTools: Write, Edit: o checkpoint deixa de depender de boa vontade,porque um survey que consegue escrever é um survey que consegue decidir.
Marketplace no próprio repo + CHANGELOG. A versão é semver explícita de
propósito: sem ela a chave de cache vira o SHA e todo push vira update. O
esquecimento de bump virou invariante, não disciplina.
Suítes: 3 → 4
scripts/guard_test.sh, 42 casos. A metade que mais importa é o que o guardadeixa passar: rollback canônico,
git add --por pathspec,revert,mv,stash push -u, e vizinhos comogit cleanup-branchegit add -Applies.Invariantes de coerência: 151 → 248. Os novos ligam cada comando bloqueado à
tabela dos dois READMEs, cada agent ao Step 0.2, e o CHANGELOG ao manifesto.
Todos foram testados contra uma regressão simulada — invariante que passa por
construção não entrou.
How to verify
Validado também em container Linux (
node:22-bookworm) e com o CLI real(v2.1.220):
plugin validatepassa nos dois manifestos, os 5 agents carregamnamespaceados, e o ciclo
marketplace add ./→list→removecompleta.O que fica de fora
Validar o plugin instalado numa limpeza real — #34. É o único teste que
nenhuma suíte alcança: o
matcherentregando a chamada ao guarda, o${CLAUDE_PLUGIN_ROOT}resolvendo do cache, o exit 2 chegando ao modelo comobloqueio.