- Início
- Documentation
- Configuração
- Contrato de Arquitetura do Cache de Varredura do Sistema de Arquivos
Contrato de Arquitetura do Cache de Varredura do Sistema de Arquivos
Este documento define o contrato atual para o cache compartilhado de varredura do sistema de arquivos implementado em Rust (crates/pi-natives/src/fs_cache.rs) e consumido pelas APIs nativas de descoberta/busca expostas para packages/coding-agent.
O que é este cache
Seção intitulada “O que é este cache”O cache armazena listas completas de entradas de varredura de diretórios (GlobMatch[]) indexadas por escopo de varredura e política de travessia, permitindo que operações de nível superior (filtragem glob, pontuação fuzzy, seleção de arquivos grep) sejam executadas sobre essas entradas em cache.
Objetivos principais:
- evitar caminhadas repetidas no sistema de arquivos para chamadas repetidas de descoberta/busca
- manter consistência entre
glob,fuzzyFindegrepquando compartilham a mesma política de varredura - permitir recuperação explícita de obsolescência para resultados vazios e invalidação explícita após mutações de arquivos
Propriedade e superfície pública
Seção intitulada “Propriedade e superfície pública”- Implementação e política do cache:
crates/pi-natives/src/fs_cache.rs - Consumidores nativos:
crates/pi-natives/src/glob.rscrates/pi-natives/src/fd.rs(fuzzyFind)crates/pi-natives/src/grep.rs
- Binding/exportação JS:
packages/natives/src/glob/index.ts(invalidateFsScanCache)packages/natives/src/glob/types.tspackages/natives/src/grep/types.ts
- Helpers de invalidação por mutação do coding-agent:
packages/coding-agent/src/tools/fs-cache-invalidation.ts
Particionamento da chave de cache (contrato rígido)
Seção intitulada “Particionamento da chave de cache (contrato rígido)”Cada entrada é indexada por:
- caminho do diretório
rootcanonicalizado - booleano
include_hidden - booleano
use_gitignore
Implicações:
- Varreduras com e sem arquivos ocultos não compartilham entradas.
- Varreduras que respeitam gitignore e varreduras com ignore desabilitado não compartilham entradas.
- Os consumidores devem passar semânticas estáveis para o comportamento de hidden/gitignore; alterar qualquer uma das flags cria uma partição de cache diferente.
A inclusão de node_modules não faz parte da chave de cache. O cache armazena entradas com node_modules incluído; a filtragem por consumidor é aplicada após a recuperação.
Comportamento de coleta da varredura
Seção intitulada “Comportamento de coleta da varredura”O preenchimento do cache utiliza um walker determinístico (ignore::WalkBuilder) configurado por include_hidden e use_gitignore:
follow_links(false)- ordenado por caminho do arquivo
.gité sempre ignoradonode_modulesé sempre coletado no momento da varredura do cache (e opcionalmente filtrado depois)- o tipo de arquivo da entrada +
mtimesão capturados viasymlink_metadata
As raízes de busca são resolvidas por resolve_search_path:
- caminhos relativos são resolvidos em relação ao cwd atual
- o destino deve ser um diretório existente
- a raiz é canonicalizada quando possível
Política de frescor e evição
Seção intitulada “Política de frescor e evição”Política global (sobrescrevível por variável de ambiente):
FS_SCAN_CACHE_TTL_MS(padrão1000)FS_SCAN_EMPTY_RECHECK_MS(padrão200)FS_SCAN_CACHE_MAX_ENTRIES(padrão16)
Comportamento:
get_or_scan(...)- se o TTL for
0: ignora o cache completamente, sempre faz varredura nova (cache_age_ms = 0) - em cache hit dentro do TTL: retorna entradas em cache +
cache_age_msdiferente de zero - em hit expirado: remove a chave, faz nova varredura, armazena entrada nova
- se o TTL for
- a aplicação do limite máximo de entradas utiliza evição por ordem de antiguidade baseada em
created_at
Reverificação rápida de resultado vazio (separada de hits normais)
Seção intitulada “Reverificação rápida de resultado vazio (separada de hits normais)”Cache hit normal:
- um cache hit dentro do TTL retorna as entradas em cache e não faz mais nada.
Reverificação rápida de resultado vazio:
- esta é uma política do lado do chamador usando
ScanResult.cache_age_ms - se o resultado filtrado/consultado estiver vazio e a idade da varredura em cache for pelo menos
empty_recheck_ms(), o chamador realiza umforce_rescan(...)e tenta novamente - destinado a reduzir resultados falso-negativos obsoletos quando arquivos foram adicionados recentemente, mas o cache ainda está dentro do TTL
Consumidores atuais:
glob: reverifica quando as correspondências filtradas estão vazias e a idade da varredura excede o limiarfuzzyFind(fd.rs): reverifica apenas quando a query não está vazia e as correspondências pontuadas estão vaziasgrep: reverifica quando a lista de arquivos candidatos selecionados está vazia
Padrões dos consumidores e uso do cache
Seção intitulada “Padrões dos consumidores e uso do cache”O cache é opt-in em todas as APIs expostas (cache?: boolean, padrão false).
Padrões atuais nas APIs nativas:
glob:hidden=false,gitignore=true,cache=falsefuzzyFind:hidden=false,gitignore=true,cache=falsegrep:hidden=true,cache=false, e a varredura de cache sempre usause_gitignore=true
Chamadores do coding-agent atualmente:
- Descoberta de candidatos a menção em alto volume habilita o cache:
packages/coding-agent/src/utils/file-mentions.ts- perfil:
hidden=true,gitignore=true,includeNodeModules=true,cache=true
- Integração de
grepno nível de ferramenta atualmente desabilita o cache de varredura (cache: false):packages/coding-agent/src/tools/grep.ts
Contrato de invalidação
Seção intitulada “Contrato de invalidação”Ponto de entrada nativo de invalidação:
invalidateFsScanCache(path?: string)- com
path: remove entradas de cache cuja raiz é um prefixo do caminho de destino - sem path: limpa todas as entradas do cache de varredura
- com
Detalhes do tratamento de caminhos:
- caminhos de invalidação relativos são resolvidos em relação ao cwd
- a invalidação tenta canonicalização
- se o destino não existir (ex.: exclusão), o fallback canonicaliza o pai e reanexa o nome do arquivo quando possível
- isso preserva o comportamento de invalidação para criação/exclusão/renomeação onde um dos lados pode não existir
Responsabilidades do fluxo de mutação do coding-agent
Seção intitulada “Responsabilidades do fluxo de mutação do coding-agent”O código do coding-agent deve invalidar após mutações bem-sucedidas no sistema de arquivos.
Helpers centrais:
invalidateFsScanAfterWrite(path)invalidateFsScanAfterDelete(path)invalidateFsScanAfterRename(oldPath, newPath)(invalida ambos os lados quando os caminhos diferem)
Callsites atuais de ferramentas de mutação:
packages/coding-agent/src/tools/write.tspackages/coding-agent/src/patch/index.ts(fluxos hashline/patch/replace)
Regra: se um fluxo muta conteúdo ou localização no sistema de arquivos e ignora esses helpers, bugs de obsolescência do cache são esperados.
Adicionando um novo consumidor de cache com segurança
Seção intitulada “Adicionando um novo consumidor de cache com segurança”Ao introduzir o uso de cache em um novo caminho de scanner/busca:
-
Use entradas estáveis de política de varredura
- decida a semântica de hidden/gitignore primeiro
- passe-as consistentemente para
get_or_scan/force_rescanpara que as partições de cache sejam intencionais
-
Trate os dados do cache como pré-filtrados apenas pela política de travessia
- aplique filtragem específica da ferramenta (padrões glob, filtros de tipo, regras de node_modules) após a recuperação
- nunca assuma que as entradas em cache já refletem seus filtros de nível superior
-
Implemente reverificação rápida de resultado vazio apenas para risco de falso-negativo obsoleto
- use
scan.cache_age_ms >= empty_recheck_ms() - tente novamente uma vez com
force_rescan(..., store=true, ...) - mantenha esse caminho separado da lógica normal de cache-hit
- use
-
Respeite o modo sem cache explicitamente
- quando o chamador desabilitar o cache, chame
force_rescan(..., store=false, ...) - não popule o cache compartilhado em um caminho de requisição sem cache
- quando o chamador desabilitar o cache, chame
-
Conecte a invalidação por mutação para qualquer novo caminho de escrita
- após escrita/edição/exclusão/renomeação bem-sucedida, chame o helper de invalidação do coding-agent
- para renomeação/movimentação, invalide tanto o caminho antigo quanto o novo
-
Não adicione controles de TTL por chamada
- o contrato atual é apenas política global (configurada por env), sem sobrescrita de TTL por requisição
Limites conhecidos
Seção intitulada “Limites conhecidos”- O escopo do cache é em memória e local ao processo (
DashMap), não persistido entre reinicializações do processo. - O cache armazena entradas de varredura, não resultados finais das ferramentas.
glob/fuzzyFind/grepcompartilham entradas de varredura apenas quando as dimensões da chave (root,hidden,gitignore) correspondem..gité sempre excluído no momento da coleta da varredura, independentemente das opções do chamador.