Pular para o conteúdo

Pipeline de Correspondência de Rulebook

Este documento descreve como o coding-agent descobre regras a partir de formatos de configuração suportados, normaliza-as em uma única estrutura Rule, resolve conflitos de precedência e divide o resultado em:

  • Regras de Rulebook (disponíveis para o modelo via system prompt + URLs rule://)
  • Regras TTSR (regras de interrupção de stream por viagem no tempo)

Ele reflete a implementação atual, incluindo semânticas parciais e metadados que são analisados mas não aplicados.

Todos os provedores normalizam os arquivos-fonte em Rule:

interface Rule {
name: string;
path: string;
content: string;
globs?: string[];
alwaysApply?: boolean;
description?: string;
ttsrTrigger?: string;
_source: SourceMeta;
}

A identidade da capability é rule.name (ruleCapability.key = rule => rule.name).

Consequência: precedência e deduplicação são baseadas apenas no nome. Dois arquivos diferentes com o mesmo name são considerados a mesma regra lógica.

src/discovery/index.ts registra automaticamente os provedores. Para rules, os provedores atuais são:

  • native (prioridade 100)
  • cursor (prioridade 50)
  • windsurf (prioridade 50)
  • cline (prioridade 40)

Carrega regras .xcsh de:

  • projeto: <cwd>/.xcsh/rules/*.{md,mdc}
  • usuário: ~/.xcsh/agent/rules/*.{md,mdc}

Normalização:

  • name = nome do arquivo sem .md/.mdc
  • frontmatter analisado via parseFrontmatter
  • content = corpo (frontmatter removido)
  • globs, alwaysApply, description, ttsr_trigger mapeados diretamente

Ressalva importante: globs é convertido como string[] | undefined sem filtragem de elementos neste provedor.

Carrega de:

  • usuário: ~/.cursor/rules/*.{mdc,md}
  • projeto: <cwd>/.cursor/rules/*.{mdc,md}

Normalização (transformMDCRule):

  • description: mantido apenas se for string
  • alwaysApply: apenas true é preservado (false torna-se undefined)
  • globs: aceita array (apenas elementos string) ou string única
  • ttsr_trigger: apenas string
  • name a partir do nome do arquivo sem extensão

Carrega de:

  • usuário: ~/.codeium/windsurf/memories/global_rules.md (nome de regra fixo global_rules)
  • projeto: <cwd>/.windsurf/rules/*.md

Normalização:

  • globs: array de strings ou string única
  • alwaysApply, description obtidos do frontmatter
  • ttsr_trigger: apenas string
  • name a partir do nome do arquivo para regras de projeto

Busca ascendentemente a partir de cwd pelo .clinerules mais próximo:

  • se diretório: carrega *.md dentro dele
  • se arquivo: carrega arquivo único como regra nomeada clinerules

Normalização:

  • globs: array de strings ou string única
  • alwaysApply: apenas se booleano
  • description: apenas string
  • ttsr_trigger: apenas string

3. Comportamento do parsing de frontmatter e ambiguidade

Seção intitulada “3. Comportamento do parsing de frontmatter e ambiguidade”

Todos os provedores usam parseFrontmatter (utils/frontmatter.ts) com estas semânticas:

  1. O frontmatter é analisado apenas quando o conteúdo começa com --- e possui um fechamento \n---.
  2. O corpo é aparado após a extração do frontmatter.
  3. Se o parsing YAML falhar:
    • um aviso é registrado,
    • o parser recorre a uma análise simples de linhas key: value (^(\w+):\s*(.*)$).

Consequências da ambiguidade:

  • O parser de fallback não suporta arrays, objetos aninhados, regras de aspas ou chaves com hífen.
  • Valores de fallback tornam-se strings (por exemplo alwaysApply: true torna-se a string "true"), então provedores que exigem tipos boolean/string podem descartar metadados.
  • ttsr_trigger funciona no fallback (chave com underscore); chaves como thinking-level não funcionariam.
  • Arquivos sem frontmatter válido ainda são carregados como regras com metadados vazios e corpo de conteúdo completo.

loadCapability("rules") (capability/index.ts) mescla as saídas dos provedores e depois deduplica por rule.name.

  • Os provedores são ordenados por prioridade decrescente.
  • Prioridade igual mantém a ordem de registro (cursor antes de windsurf conforme discovery/index.ts).
  • A deduplicação é por primeiro encontrado: o primeiro nome de regra encontrado é mantido; itens posteriores com o mesmo nome são marcados como _shadowed em all e excluídos de items.

A ordem efetiva dos provedores de regras atualmente é:

  1. native (100)
  2. cursor (50)
  3. windsurf (50)
  4. cline (40)

Dentro de um provedor, a ordem dos itens vem da ordenação do resultado glob de loadFilesFromDir mais a ordem explícita de push. Isso é determinístico o suficiente para uso normal, mas não é explicitamente ordenado no código.

Diferenças notáveis na ordem das fontes:

  • native adiciona diretórios de configuração do projeto e depois do usuário.
  • cursor adiciona resultados do usuário e depois do projeto.
  • windsurf adiciona global_rules do usuário primeiro, depois regras do projeto.
  • cline carrega apenas a fonte .clinerules mais próxima.

5. Divisão em buckets de Rulebook, Always-Apply e TTSR

Seção intitulada “5. Divisão em buckets de Rulebook, Always-Apply e TTSR”

Após a descoberta de regras em createAgentSession (sdk.ts):

  1. Todas as regras descobertas são examinadas.
  2. Regras com condition (chave de frontmatter; ttsr_trigger / ttsrTrigger aceitos como fallback) são registradas no TtsrManager.
  3. Uma lista separada rulebookRules é construída com este predicado:
!registeredTtsrRuleNames.has(rule.name) && !rule.alwaysApply && !!rule.description
  1. Uma lista alwaysApplyRules é construída:
!registeredTtsrRuleNames.has(rule.name) && rule.alwaysApply === true
  • Bucket TTSR: qualquer regra com condition (description não é obrigatória). Tem prioridade sobre os outros buckets.
  • Bucket always-apply: alwaysApply === true, não é TTSR. Conteúdo completo injetado no system prompt. Resolvível via rule://.
  • Bucket rulebook: deve ter description, não deve ser TTSR, não deve ser alwaysApply. Listado no system prompt por nome+descrição; conteúdo lido sob demanda via rule://.
  • Uma regra com tanto condition quanto alwaysApply vai apenas para TTSR (TTSR tem prioridade).
  • Uma regra com tanto alwaysApply quanto description vai apenas para always-apply (não para rulebook).

6. Como os metadados afetam as superfícies em tempo de execução

Seção intitulada “6. Como os metadados afetam as superfícies em tempo de execução”
  • Obrigatório para inclusão no rulebook.
  • Renderizado no bloco <rules> do system prompt.
  • Description ausente significa que a regra não está disponível via rule:// e não é listada nas regras do system prompt.
  • Transportado na Rule.
  • Renderizado como entradas <glob>...</glob> no bloco de regras do system prompt.
  • Exposto no estado da UI de regras (lista de modo extensions).
  • Não é aplicado para correspondência automática neste pipeline. Não há matcher de glob em tempo de execução selecionando regras pelo arquivo atual/alvo da ferramenta.
  • Analisado e preservado pelos provedores.
  • Usado na exibição da UI (rótulo de trigger "always" no gerenciador de estado de extensões).
  • Usado como condição de exclusão de rulebookRules.
  • O conteúdo completo da regra é auto-injetado no system prompt (antes da seção de regras do rulebook).
  • A regra também é acessível via rule://<name> para releitura.
  • Mapeado para rule.ttsrTrigger.
  • Se presente, a regra é direcionada ao gerenciador TTSR, não ao rulebook.

buildSystemPromptInternal recebe tanto rules (rulebook) quanto alwaysApplyRules.

As regras always-apply são renderizadas primeiro, injetando seu conteúdo bruto diretamente no prompt.

As regras de rulebook são renderizadas em uma seção # Rules com:

  • Read rule://<name> when working in matching domain
  • O name, description e lista opcional de <glob> de cada regra

Isso é consultivo/contextual: o texto do prompt solicita que o modelo leia as regras aplicáveis, mas o código não aplica a correspondência de glob.

RuleProtocolHandler é registrado com:

new RuleProtocolHandler({ getRules: () => [...rulebookRules, ...alwaysApplyRules] })

Implicações:

  • rule://<name> resolve contra tanto rulebookRules quanto alwaysApplyRules.
  • Regras exclusivamente TTSR e regras sem description e sem alwaysApply não são acessíveis via rule://.
  • A resolução é por correspondência exata de nome.
  • Nomes desconhecidos retornam erro listando os nomes de regras disponíveis.
  • O conteúdo retornado é o rule.content bruto (frontmatter removido), tipo de conteúdo text/markdown.

9. Semânticas parciais / não aplicadas conhecidas

Seção intitulada “9. Semânticas parciais / não aplicadas conhecidas”
  1. As descrições dos provedores mencionam arquivos legados (.cursorrules, .windsurfrules), mas os caminhos de código do carregador atual não leem realmente esses arquivos.
  2. Os metadados globs são expostos ao prompt/UI mas não são aplicados pela lógica de seleção de regras.
  3. A seleção de regras para rule:// inclui regras de rulebook e always-apply, mas não regras exclusivamente TTSR.
  4. Avisos de descoberta (loadCapability("rules").warnings) são produzidos mas createAgentSession atualmente não os exibe/registra neste caminho.