Delphi AI Skills 0.3.0: a linguagem, e a auditoria do código
🇮🇹 Italiano • 🇬🇧 English • 🇪🇸 Español • 🇩🇪 Deutsch
A 0.3.0 do delphi-ai-skills traz as skills Delphi genéricas anunciadas no lançamento: dez skills open source que ensinam ao Claude Code, ao Codex, ao Cursor e ao Gemini a linguagem e a RTL, e como auditar uma unit que ninguém olha há oito anos.
No último parágrafo do primeiro anúncio, em julho, estava escrito que as sete skills de então eram deliberadamente verticais no DelphiMVCFramework, que aquele era o ponto de partida e não a chegada, e que as seguintes tratariam de Delphi como plataforma. A 0.3.0 é a primeira parte daquele pedaço que faltava.
Agora as skills são dez. Duas das três novas não têm relação com o DMVCFramework: valem para qualquer código Delphi: uma form VCL, um serviço Windows, uma biblioteca, uma unit em que ninguém encosta desde 2004.
Em resumo
delphi: a linguagem e a RTL, sem pressupor framework nem layout de projeto: version gating, memória e lifetime, strings, exceções, generics, threading.delphi-code-smells: a passada de auditoria no código que você já tem. Warnings do compilador, memory leaks, access violations, double frees, e para cada defeito o jeito de encontrá-lo.dmvcframework-jsonrpc: JSON-RPC 2.0, da publicação da classe até o clienteIMVCJSONRPCExecutor.- Continua Apache-2.0, continua Markdown puro, continua rodando em Claude Code, Codex, Cursor, Gemini CLI e em qualquer agente que saiba ler um arquivo.
- Repositório: github.com/danieleteti/delphi-ai-skills
A skill delphi: aquela que não pressupõe nada
Um agente de IA leu muito mais C# e TypeScript do que Object Pascal. O resultado não é código errado de um jeito evidente: é código que parece Delphi. Tem os begin no lugar certo, as maiúsculas certas, mas por dentro tem s[0] para ler o primeiro caractere, um try ... except ... finally ... end num bloco só, uma string multilinha que no Delphi 11 não existe, e outros ruídos que vieram de outras linguagens.
A skill delphi cobre exatamente essa camada, e a parte que se paga primeiro é o version gating. O modelo não sabe em qual versão você compila, e na mesma unit mistura épocas diferentes sem perceber: uma sintaxe que chegou com o Florence ao lado de um idioma que se escrevia em 2004.
// Delphi 12 Athens e posteriores. No 11 Alexandria não compila.
var lSql := '''
select * from customers
''';
// Delphi 13 Florence e posteriores: if-then-else como expressão.
X := if Left < 100 then 22 else 45;
A skill traz a tabela das CompilerVersion release por release, e antes disso a regra que a torna útil: a versão de destino se apura, não se supõe. O agente pergunta ao compilador, porque dcc32.exe --version imprime exatamente a CompilerVersion que interessa; se não chegar lá, olha quais Studio estão instalados; e se encontrar mais de um, ou nenhum, faz uma única pergunta: 11 Alexandria, 12 Athens ou 13 Florence? O .dproj não vale como resposta, porque <ProjectVersion> é a versão do formato do arquivo de projeto, não do produto, e um projeto salvo pela última vez por uma IDE antiga abre igualzinho numa IDE nova.
Daí em diante ele se adapta. Num 13 Florence confirmado, o if como expressão se usa e pronto, e embrulhá-lo num {$IF} é ruído. No 11 não pode aparecer. E quando a resposta não vem, a skill escreve para 11 Alexandria (o mínimo que ela assume), põe a guarda explícita e declara isso a você, em vez de deixar que você descubra a escolha na hora de compilar.
{$IF CompilerVersion >= 36} // Delphi 12 Athens e posteriores
...
{$ENDIF}
Depois vem o catálogo dos erros que um LLM comete em Delphi com uma regularidade quase comovente, cada um com a forma certa ao lado e o motivo. Algumas linhas, só para dar a ideia:
| Errado | Certo | Por quê |
|---|---|---|
s[0] para o primeiro caractere |
s[1], ou s.Chars[0] |
string tem base 1, mas TStringHelper (Chars, IndexOf, Substring) é compilado com base 0. Duas bases de indexação no mesmo tipo. |
return X; |
Result := X;, ou Exit(X) |
Result é uma variável implícita, não uma instrução. Lida antes de ser atribuída, devolve lixo. |
try ... except ... finally ... end |
aninhar os dois | Um bloco só não pode ter os dois. É erro de sintaxe, não questão de gosto. |
with lObj do ... |
uma variável local | with mascara os identificadores: um campo acrescentado a lObj seis meses depois toma para si um nome do contexto externo, em silêncio, e compila. |
procedure Foo(AText: string) |
procedure Foo(const AText: string) |
Num tipo gerenciado, const evita refcount e cópia. |
TStringList.Create esperando que libere os Objects[] |
TStringList.Create(True) |
Os dois contêineres têm defaults opostos: TObjectList<T> é dono, TStringList não. |
O grosso do material está em seis arquivos reference/ que o agente só abre quando a tarefa exige: memória e lifetime, strings e encoding, exceções, generics e RTTI, concorrência, estilo. A janela de contexto continua livre enquanto não for realmente necessária, que é o motivo pelo qual uma skill é um arquivo em disco e não um bloco colado no topo do chat.
Tudo foi copiado dos fontes RTL/VCL instalados em disco, ou confirmado na docwiki. A skill também sabe que pode estar incompleta, e quando precisa de uma assinatura que não tem, carrega a ordem das operações escrita por dentro: primeiro lê o fonte, depois a docwiki, e se ainda assim não tiver certeza, diz. Não tenho certeza de que TFoo.Bar exista, confira em System.Classes.pas é uma resposta útil. Uma resposta confiante e errada não é.
E ela lê a árvore certa: se você compila com o 12 Athens, a verificação tem de ser feita em Studio\23.0\source\rtl, não na cópia do mesmo arquivo que está debaixo de 37.0. A RTL cresce release após release, e um tipo ou um overload que existe no Florence pode simplesmente não existir no Athens. Vale também para o que as próprias skills carregam: o container do DUnitX com que se faz uma build falhar num memory leak se chama TDUnitXServiceLocator na versão distribuída com o 13 Florence e TDUnitXIoC na distribuída com o 12 Athens, com exatamente a mesma chamada embaixo. Um detalhe desses ninguém guarda de cabeça: vai lá e confere.
Uma declaração, porém, é só metade da resposta. Diz quantos parâmetros são necessários e de que tipo, e se cala sobre todo o resto: quem libera o quê, em que ordem as coisas devem ser chamadas, qual é a forma idiomática. Para isso é preciso um ponto de chamada de verdade, e as skills o procuram nesta ordem: primeiro o seu código, que traz junto também as convenções da casa a respeitar, depois os próprios fontes (a RTL usa o tempo todo as próprias APIs, então um grep devolve exemplos que funcionam e não prosa de documentação), depois os samples do framework ou a docwiki.
E se o agente não sabe onde estão esses fontes, a regra é perguntar em vez de chutar. A resposta, porém, não se perde no fim da sessão: depois de verificar que o caminho existe, o agente propõe escrevê-lo no arquivo de instruções que ele já lê, o CLAUDE.md do projeto ou o AGENTS.md, num bloco só dele:
<!-- delphi-local-sources -->
Delphi RTL/VCL source: C:\Program Files (x86)\Embarcadero\Studio\23.0\source (12 Athens, CompilerVersion 36.0)
DelphiMVCFramework checkout: C:\DEV\dmvcframework (sources/ + samples/)
<!-- /delphi-local-sources -->
Esse bloco é a primeira coisa que o agente olha quando a sessão começa, e a pergunta passa a ser feita uma vez por projeto em vez de uma vez por dia. Se um caminho deixar de existir, ou se você trocar de versão do Delphi, ele avisa e pergunta de novo: prefere admitir que não sabe a pescar alguma coisa da memória.
A skill delphi-code-smells: primeiro a máquina, depois a opinião
A outra metade do ofício não é escrever código, é olhar para o que já existe. É aqui que os agentes se saem pior, e não porque errem: porque são educados. Você pede uma review e recebe uma página de observações sobre nomes de variáveis, a ordem das uses e o tamanho dos métodos. Zero leaks. Uma review que devolve quinze notas de estilo e nenhum problema de lifetime não é uma review, é um parecer.
A delphi-code-smells impõe uma ordem de ataque, e os dois primeiros pontos não envolvem juízo humano nenhum:
- Compilar com warnings e hints ligados, e ler cada linha da saída. De graça, objetivo, e com retorno maior do que qualquer outra coisa.
- Executar com
ReportMemoryLeaksOnShutdown := True, e se existir uma suíte de testes, executar a suíte. - Ownership na mão: cada
Createda unit, quem o libera, em quais caminhos, inclusive naquele que levanta exceção. - Tratamento de exceções: cada
exceptsemon, cada handler vazio, cadatry/exceptque queria ser umtry/finally. - Concorrência: tudo o que é alcançável a partir de um
TThread.Executeou do corpo de umTTask.Run. - O resto. Naming,
with, métodos longos, números mágicos.
A regra que segura a lista inteira está escrita na skill de um jeito pouco diplomático: um leak que o compilador não vê ganha de uma convenção de nomenclatura, sempre. E cada defeito tem de ser relatado com o que custa em runtime, não com o quanto é feio de ler.
O defeito número um do ranking é este, e continuo vendo toda semana nas minhas consultorias:
// ERRADO: o construtor está DENTRO do try
try
lList := TStringList.Create;
...
finally
lList.Free; // se Create levantar exceção, aqui se chama Destroy em memória não inicializada
end;
// CERTO
lList := TStringList.Create;
try
...
finally
lList.Free; // .Free é nil-safe: "if x <> nil then x.Free" é ruído
end;
Duas linhas trocadas de lugar. A primeira versão produz um access violation só quando o construtor falha, ou seja, quase nunca, ou seja, na terça de manhã, no cliente que não reinicia o servidor há oito meses.
A skill traz também a passada de cinco minutos: um punhado de greps postos em ordem de retorno, do construtor dentro do try à exceção engolida por um except end, do FreeAndNil numa variável local ao FreeOnTerminate. São os golpes que um humano não tem vontade de dar e que uma máquina dá em três segundos.
rg -n -U 'try\b[^;]*?\n\s*\w+\s*:=\s*T\w+\.Create' # construtor DENTRO do try
rg -n -P '(?s)except\s*(//[^\n]*\n\s*)*end' # exceção engolida
rg -n 'FreeOnTerminate' # lifetime das threads
Vem depois a parte que torna a auditoria repetível em vez de episódica: como se lê de verdade o relatório do memory manager, quando é preciso o RegisterExpectedMemoryLeak, e como se configura o DUnitX para que um leak faça a build falhar. Um leak encontrado uma vez é um dia de trabalho. Um leak que a partir de amanhã quebra o pipeline é uma classe de bug encerrada.
Uma última regra, que é a minha preferida porque vale também para as pessoas: nunca relate um smell sobre o qual você não sabe dizer como se encontra. Se não existe um código de warning, uma ferramenta ou um grep que o demonstre, é uma preferência, e ninguém tinha pedido.
E também JSON-RPC
A terceira skill nova, dmvcframework-jsonrpc, volta para dentro do perímetro do framework: um endpoint JSON-RPC 2.0 em DelphiMVCFramework é uma classe Delphi comum publicada num segmento de URL, sem atributos de roteamento por método. A skill cobre o que é chamável e o que não é, a distinção entre function (request) e procedure (notification), os parâmetros nominais e posicionais, as três regras de ownership sobre quem libera o quê, os códigos de erro, os hooks, e o cliente IMVCJSONRPCExecutor para chamar aquele endpoint a partir do Delphi.
As dez skills, numa tabela
| Skill | O que cobre |
|---|---|
delphi |
A linguagem e a RTL: version gating, inline var, memória e lifetime, strings e encoding, exceções, System.Generics.Collections, RTTI, threading, convenções. |
delphi-code-smells |
A review: warnings e hints que indicam um bug de verdade, warnings-as-errors, busca de leaks, como fazer uma build falhar num leak, análise estática, catálogo de smells com o jeito de encontrar cada um. |
dmvcframework |
O núcleo do framework: bootstrap e engine, controllers e functional actions, roteamento, IMVCResponse, ownership, ORM ActiveRecord, Repository, container de DI, validação, middleware, JWT, SSE, dotEnv. |
dmvcframework-minimal-api |
Rotas como métodos anônimos: grupos de rotas (Prefix, MapGet, MapPost), binding guiado pelos tipos, upload, endpoint filters e HTTP filters, .AsWeb. |
dmvcframework-webapp |
Aplicações web server-side: TemplatePro, herança de templates, fragments, ViewData, login com cookie/JWT, arquivos estáticos, helpers HTMX do lado Delphi. |
dmvcframework-ui |
A camada de apresentação do wizard: Bootstrap 5.3, baselayout.html, os tokens de style.css, dark mode, toasts. |
dmvcframework-security |
Secure coding do lado servidor: access control e IDOR, mass assignment, SQL injection, XSS no TemplatePro, CSRF, path traversal, upload, SSRF, cabeçalhos, hardening do JWT, segredos. |
dmvcframework-jsonrpc |
JSON-RPC 2.0: publicação, request e notification, parâmetros, ownership, erros, hooks, cliente. |
dmvcframework-testing |
DUnitX, IMVCServer in-process, IMVCRESTClient, testes CRUD, de autenticação e de autorização, fixtures de banco de dados. |
htmx-skill |
O índice de cada página da documentação oficial do htmx.org, para que o agente leia a página certa em vez de lembrar do htmx do seu próprio jeito. |
As duas skills Delphi não têm requisito nenhum: nenhum layout de projeto, nenhum framework. As sete de DMVCFramework partem de um projeto criado com o wizard da IDE, e a dmvcframework-security é puxada de ofício por qualquer endpoint que receba entrada de um cliente.
Como se instalam e como se usam
git clone https://github.com/danieleteti/delphi-ai-skills.git
cd delphi-ai-skills
install_in_claude.bat
Para o Claude Code termina aqui: as skills são descobertas sozinhas. Para os outros agentes existem install_in_codex.bat, install_in_cursor.bat e install_in_gemini.bat, que copiam as skills e escrevem o ponteiro no arquivo de instruções certo (AGENTS.md, .cursor/rules/*.mdc, GEMINI.md), porque esses agentes não as descobrem sozinhos. Cada script aceita um caminho de projeto, se você preferir versionar as skills no repositório e entregá-las a todo o time:
install_in_claude.bat C:\DEV\meu-projeto
As skills não se “chamam”: você descreve a tarefa e o agente escolhe. O gatilho mais forte é nomear a tecnologia. “Ache o leak” é ambíguo, “ache o leak nesta unit Delphi” não é.
Esta unit compila no Delphi 11, ou estou usando sintaxe que só existe do Athens em diante? Este serviço perde memória ao longo de alguns dias: ache o ponto. Faça uma review desta unit e me diga o que está de fato errado, não o estilo. Quais warnings do compilador eu estou ignorando que escondem um bug de verdade? Faça a build falhar quando houver um memory leak. Este código roda numa thread e mexe num label VCL: o que tem de errado?
TObjectList<T>comOwnsObjectsouTList<T>simples?
Quando você quiser a garantia, basta nomear a skill: no Claude Code com /delphi ou /delphi-code-smells, nos outros citando o caminho (“leia skills/delphi-code-smells/SKILL.md, depois revise esta unit”). Funciona em qualquer lugar, porque é um arquivo de texto, não uma função.
Um conselho que vale os dez segundos que custa: se você tem os fontes do DelphiMVCFramework em disco, diga isso ao agente no começo da sessão (“os fontes do DMVCFramework estão em C:\DEV\dmvcframework”). As skills são instruídas a verificar em vez de adivinhar, e com os fontes ao alcance da leitura verificam muito melhor.
Por que as skills não podem conter um nome inventado
Há um problema de fundo, num projeto cujo único objetivo é impedir que um agente invente nomes de API: se quem inventa um é a skill, o estrago é pior do que antes, porque agora o erro tem o ar autoritativo da documentação.
Por isso as skills não saem se não passarem por um pipeline que as confronta com o código de verdade: os fontes do DelphiMVCFramework e a RTL de todas as versões de Delphi suportadas. Cada nome citado precisa ser um nome que existe lá dentro. Se um só não estiver, a release para, e não é um parecer que se possa discutir em review: ou o nome está nos fontes ou não está nas skills.
É a verificação que fez emergir o defeito do DUnitX do 12 Athens contado mais acima, e o fez emergir aqui em vez de na sua casa, que é exatamente o ponto.
O que uma verificação dessas não consegue dizer é se aquela API está sendo usada bem: para isso é preciso um compilador, e para um erro de ownership é preciso executar o programa. É o primeiro dos três níveis com que as skills são controladas, e é aquele que custa tão pouco que pode ficar ligado o tempo todo.
Materiais e vídeos no Patreon
As skills dizem ao agente o que é verdade. Fica de fora o como: como é uma sessão de trabalho de verdade, onde convém parar, o que perguntar e em que ordem, quando o agente está pegando um caminho que vai lhe custar duas horas.
Nas próximas semanas vou publicar bastante material, escrito e em vídeo, na página do Patreon do DelphiMVCFramework: sessões inteiras sobre Delphi e sobre DMVCFramework com as skills trabalhando, com os pontos em que algo dá errado deixados lá dentro, porque são a parte que se aprende.
O repositório continua Apache-2.0 e completo: nada do que é preciso para usar as skills está atrás de uma assinatura. O Patreon é o lugar onde você encontra o material explicativo, e é também o modo com que quem quiser sustenta o trabalho no DMVCFramework e em tudo o que gira em volta dele. Se lhe for útil, o canal é esse. Se você preferir pegar as skills e seguir o seu caminho, está ótimo do mesmo jeito: é exatamente para isso que elas estão no GitHub.
Perguntas frequentes sobre o delphi-ai-skills 0.3.0
O que há de novo na 0.3.0 do delphi-ai-skills?
Três skills a mais em relação à primeira release, num total de dez: delphi (a linguagem e a RTL, sem framework nenhum), delphi-code-smells (a review do código existente) e dmvcframework-jsonrpc (JSON-RPC 2.0). A 0.3.0 também corrige duas afirmações erradas na reference sobre memória da skill delphi.
Preciso usar DelphiMVCFramework para usar estas skills?
Não, e isso já estava previsto desde o primeiro anúncio. delphi e delphi-code-smells não pressupõem framework nenhum nem layout de projeto nenhum: valem para uma form VCL, um serviço Windows, uma biblioteca, uma unit legada. As outras sete continuam específicas do DelphiMVCFramework.
O que faz a skill de auditoria do código Delphi?
A delphi-code-smells põe a review na ordem certa: primeiro o compilador com warnings e hints ligados, depois a execução com ReportMemoryLeaksOnShutdown := True, depois a ownership na mão, as exceções, a concorrência e só no fim o estilo. Cobre os warnings que indicam um bug de verdade (com o código, por exemplo W1035), $WARN e os warnings-as-errors, como se lê um relatório de leak, como se faz uma build falhar num leak com DUnitX, os analisadores estáticos de terceiros, e um catálogo de defeitos em que cada item diz o que custa em runtime e como se encontra.
As skills cuidam de segurança?
Do lado do servidor sim, e é a dmvcframework-security: uma dependência obrigatória de todas as skills DMVCFramework, aplicada a qualquer endpoint que receba entrada de um cliente. Cobre access control e IDOR, mass assignment, SQL injection, XSS no TemplatePro, CSRF, path traversal, upload, SSRF e open redirect, cabeçalhos de segurança, hardening do JWT e gestão dos segredos. Já a delphi-code-smells é uma auditoria de defeitos, não uma auditoria OWASP: cuida de leaks, access violations, double frees e resultados silenciosamente errados.
Em qual versão do Delphi elas funcionam?
No 11 Alexandria, no 12 Athens e no 13 Florence. A skill apura qual é a sua: interroga o compilador com dcc32.exe --version, na falta disso olha quais Studio estão instalados, e se ficar ambíguo pergunta a você qual usa, em vez de chutar. Com a versão conhecida, ela se adapta. Quando a resposta não vem, o alvo é o Delphi 11 Alexandria, com uma guarda {$IF CompilerVersion >= ...} em volta de tudo o que depende da versão. O conteúdo da skill é verificado sobre os fontes RTL/VCL do Delphi 13 Florence. As skills DMVCFramework apontam para a 3.5.0 (silicon).
Com quais agentes de IA elas funcionam? Claude Code, Codex, Cursor, Gemini CLI, Windsurf, Continue e qualquer agente capaz de ler instruções em Markdown. O repositório inclui o script de instalação para os quatro primeiros. Nos agentes diferentes do Claude Code o carregamento depende do quanto aquele agente respeita o próprio arquivo de instruções, então convém nomear a tecnologia no prompt, ou então a própria skill.
Como eu sei que as skills não contêm nomes de API inventados? Porque não podem. Antes de cada release, um pipeline confronta cada nome citado nas skills com o código de verdade: os fontes do DelphiMVCFramework e a RTL das versões de Delphi suportadas. Se um único identificador não existir lá dentro, aquela release não sai. Não é uma releitura atenta feita por alguém competente, é um portão: a mesma disciplina que as skills impõem ao agente, aplicada às skills. Na prática, quando uma skill afirma alguma coisa sobre uma API, aquela afirmação já foi confrontada com o código em vez de com a memória de alguém. O que a verificação não prova é que a API esteja sendo usada bem: para isso são necessários o compilador e a execução, que são os dois níveis acima.
O caminho pela frente
Ainda estamos em 0.x, e a forma do conjunto deve ser considerada instável: as skills poderão ser divididas, unidas, renomeadas ou removidas à medida que o uso real mostrar o que serve de fato. A 1.0.0 chegará quando o conjunto tiver se provado em projetos reais suficientes. Enquanto isso, os relatos valem mais do que qualquer roadmap: se uma skill fez você escrever código errado, esse é o defeito que eu quero ver primeiro.
O critério para contribuir continua sendo um só: cada afirmação deve ser verificável nos fontes, citando o arquivo. Nenhum nome de API escrito de memória. É a disciplina que pedimos aos agentes, e seria curioso não aplicá-la a nós mesmos.
O projeto está aqui: github.com/danieleteti/delphi-ai-skills. Issues e pull requests são bem-vindas.
Um agente que escreve Delphi sem saber Delphi produz código que compila mal e envelhece pior. Um agente instruído produz código que você pode ler daqui a dois anos sem se perguntar quem foi que escreveu. A diferença, por ora, são dez arquivos Markdown.
Comments
comments powered by Disqus