spec 1.0 · normativo
A Superfície Legível por Máquina
Cinco artefatos tornam a camada de contexto legível para as ferramentas. Todo o restante é prosa destinada a humanos, que também pode ser lida por agentes; esses cinco artefatos formam o contrato usado na construção das ferramentas.
| Artefato | Local padrão | Schema |
|---|---|---|
| Manifesto | leji.json (raiz do repositório, fixo) |
context-manifest.schema.json |
| Índice de contexto | <root>/context-index.json |
context-index.schema.json |
| Changelog de contexto | <root>/context-changelog.json |
context-changelog.schema.json |
| Perfis de agente | <root>/agents/*.md (frontmatter) |
agent-profile.schema.json |
| Registros de decisão | <root>/decisions/*.md (frontmatter) |
decision-record.schema.json |
Todos os locais, com exceção do manifesto, são declarados no próprio manifesto; a tabela apresenta os valores padrão.
Requisitos#
- Manifesto.
leji.jsonMUST existir na raiz do repositório e ser válido segundo seu schema. É o único nome de arquivo fixo no Leji: o arquivo que o ferramental procura de forma confiável. - Índice. Uma camada de contexto que declara conformidade
indexedou acima MUST carregar um índice de contexto que seja gerado, nunca mantido à mão: o ferramental resolve os arquivos de índice de categoria (categories.<id>.indexes, conforme content-categories.md) para os documentos que eles listam e escreve uma entrada por documento governado. Cada entrada carrega umidestável, umpath, umtitlee um identificador decategory. Um gerador SHOULD emitir também okinddo documento (intentourecord); ele é opcional no schema para que um índice escrito antes de os tipos existirem continue válido, e um consumidor trata um valor ausente comointent. A entrada de um registro carrega ainda suadatequando o documento declara umadateválida no frontmatter; o gerador obtém as datas apenas do frontmatter, nunca da prosa ou de convenções de nome de arquivo. Um índice desatualizado (que não corresponde mais àquilo para que os arquivos de índice resolvem) MUST ser tratado como uma falha de validação. Uma hospedeira que declarafederation.mountscarrega também, no mesmo índice, um arraymountsde nível superior: um registro de roteamento por mount (name,source,pin,trackingRefquando declarado,owner,rolequando declarado, e os metadados de roteamentocategories/topics/requiredWhen). Esses são apenas registros de roteamento; o ferramental MUST NOT copiar as entradas ou a prosa de uma irmã para o índice da hospedeira, e um registro de mount não carrega nada que o público da hospedeira não possa ver (conforme distribution.md, Mounts restritos). - Changelog. Uma camada de contexto que declara conformidade
indexedou acima MUST carregar um changelog legível por máquina das mudanças da camada de contexto. As entradas carregam umidestável, umadateem UTC, umtype, umsummaryde uma linha e ospathsafetados. A ordem canônica é derivada, não posicional: o ferramental MUST ordenar as entradas por(date, id)de forma ascendente, e a posição no array não tem significado. Como oidé único dentro do changelog (Identificadores),(date, id)é uma ordem total mesmo quando duas mudanças compartilham umadate. As entradas sobreviventes são imutáveis: o ferramental MUST tratar a modificação de uma entrada publicada como uma falha de validação sempre que puder estabelecer o estado anterior, e reordenar o array não é uma modificação. Estabelecer esse estado exige uma linha de base distinta para comparação; onde o ferramental de referência tem apenas a revisão atual, como em um checkout comum de integração contínua, a modificação não é visível para ele e é a revisão do conjunto de mudanças que a pega (ver conformance.md). O changelog é uma superfície de recência, não um arquivo histórico: uma camada de contexto de vida longa SHOULD compactá-lo em vez de deixá-lo crescer sem limite, e MAY compactá-lo a qualquer momento removendo entradas a partir da ponta mais antiga dessa ordem, desde que o mesmo conjunto de mudanças acrescente uma entrada do tipocompactioncujo campocompactedregistre a contagem e o primeiro e o último ids removidos. A remoção de qualquer coisa que não sejam as entradas mais antigas, a remoção sem uma entrada de compactação e a compactação até deixar o arquivo vazio são falhas de validação. A disciplina de somente acréscimo é chaveada por conjunto viaide verificada contra o estado anterior commitado, então precisa de git no momento da escrita; o arquivo em si permanece livre de git para os consumidores, e o histórico do git guarda o registro completo. Um conjunto de mudanças que toca documentos governados (aqueles para os quais os arquivos de índice de categoria resolvem) MUST acrescentar uma entrada cujospathscubram os caminhos governados que ele mudou, de modo que todo documento governado alterado caia sob alguma entrada acrescentada: a disciplina de somente acréscimo mantém as entradas publicadas imutáveis, e esta regra de cobertura mantém o registro completo. Um changelog legível por humanos MAY existir ao lado; o registro JSON é o que o ferramental lê. - Artefatos de frontmatter. Perfis de agente e registros de decisão são documentos markdown cujo frontmatter YAML é válido segundo seus schemas. O corpo em prosa permanece livre; o frontmatter é o contrato de máquina. Perfis ou decisões em JSON puro MUST NOT ser exigidos: pessoas leem esses documentos.
- Identificadores. Todos os valores de
idMUST ser estáveis uma vez publicados: renomeações e movimentações atualizam opath, nunca oid. Os identificadores são minúsculos, separados por hífen e únicos dentro de seu tipo de artefato. Oidde uma entrada de índice gerada é derivado nesta ordem de prioridade: oiddo frontmatter do documento, se ele declarar um; senão, oidque o índice armazenado já carrega para aquele mesmo caminho ou, no caso de uma movimentação pura que preserva o conteúdo, para aquele mesmo conteúdo; senão, um slug do nome do arquivo, com colisões resolvidas dentro do diretório pai. O primeiro que existir vence, de modo que umidpublicado sobrevive a uma renomeação ou movimentação e só um documento inteiramente novo cunha um novo. Um documento que possa ser movido e editado em um mesmo conjunto de mudanças SHOULD declarar umidno frontmatter: só o frontmatter prende o id ao longo de uma mudança simultânea de caminho e conteúdo (os fallbacks por caminho e por hash erram os dois), e o ferramental avisa (id-vanished) quando um id armazenado desaparece, para que as referências penduradas que ele deixa sejam pegas. - Marcas de tempo. Os valores de
datedo changelog são ISO 8601 em UTC: ou uma data de calendárioYYYY-MM-DD(ordenada como o início daquele dia,T00:00:00Z) ou uma marca de tempo em segundos inteiros terminada emZ(por exemplo2026-06-13T15:04:05Z). Horários sem fuso, deslocamentos fora de UTC e frações de segundo não são permitidos: frações de segundo quebrariam a garantia de que uma ordenação lexical dedateé uma ordenação cronológica, já que…05.1Zordena antes de…05Zsendo posterior. Todo campo de data em todo artefato é limitado ao calendário, então um mês13ou um dia99é inválido. As datas dos demais artefatos seguem ISO 8601 e MAY conter apenas a data. Os caminhos são no estilo POSIX, relativos à raiz do repositório, sem./inicial. - Todo artefato JSON exceto o manifesto MUST declarar a linha de schema contra a qual foi escrito (
schemaVersion), conforme versioning.md; o manifesto declara a linha de especificação que tem como alvo com a chave autonomeadoraleji. - Superfícies derivadas herdam as restrições de acesso. O índice, o changelog, o visualizador gerado e qualquer visão compilada ou exportada construída a partir do conteúdo da camada de contexto são superfícies derivadas, assim como a saída que um agente produz a partir desse conteúdo. Uma superfície derivada carrega as restrições de acesso do conteúdo mais restrito do qual ela se alimenta. Uma superfície derivada MUST NOT ser escrita ou copiada para um local com público mais amplo que o desse conteúdo sem uma etapa explícita e revisada de remoção do conteúdo restrito que produza uma superfície separada para aquele público, e um agente MUST NOT citar nem resumir contexto restrito em uma superfície de público mais amplo ou menos restrita (um pull request, um ticket, um chat, uma mensagem de commit ou uma camada de contexto pública). O índice de uma camada de contexto restrita pode ser tão sensível quanto a prosa dela: títulos, caminhos e resumos todos a descrevem. Esta é uma restrição sobre as pessoas e os agentes que operam o ferramental, não uma verificação que o ferramental faz: Leji não define nenhum modelo de público que uma ferramenta possa ler para calcular “público mais amplo” (o acesso pertence ao sistema de controle de versão, conforme governance.md, Fronteira de acesso), então o SDK de referência não a impõe e o máximo que qualquer ferramenta faz é avisar (a exportação do visualizador avisa para hospedá-lo de forma privada).
Roteamento de tarefas#
O índice, as atribuições de categoria e os registros de decisão existem para que um agente possa carregar a fatia de contexto de que uma tarefa precisa, em vez da árvore inteira. Esta seção define, normativamente, como o escopo de uma tarefa seleciona essa fatia. É o único algoritmo de roteamento a que o restante da especificação se refere: a seção Carregamento do perfil de boot (boot-profile.md) aponta um agente para cá em linguagem de tarefa, o escopo de registro de decisão (decisions.md) é casado por ele, e a leitura federada (distribution.md) o reutiliza para decidir quais irmãs uma tarefa toca. O roteamento lê contexto; ele não é um envelope de tarefa nem um protocolo de execução, que ficam fora da 1.0 (ver README.md, Limite de extensão).
- Entrada. O escopo de uma tarefa é o conjunto de caminhos POSIX relativos à raiz do repositório que a tarefa lê ou muda (normalizados conforme o Requisito 6: estilo POSIX, relativos à raiz, sem
./inicial), junto com quaisquer categorias que a tarefa nomeie explicitamente e quaisquer tópicos que a tarefa nomeie explicitamente. Os tópicos são entradas explícitas: o algoritmo nunca os deriva de caminhos, categorias, prosa ou conteúdo. Como um agente ou uma ferramenta deriva o escopo a partir da tarefa está fora do escopo normativo; o casamento descrito abaixo, não. - Casamento de caminho (lexical, bidirecional). Um caminho declarado e um caminho de tarefa casam quando, após a normalização (estilo POSIX, relativo à raiz, sem
./inicial, com qualquer/final removida), as duas strings são iguais, ou uma é ancestral por prefixo de caminho da outra: a mais curta é igual à mais longa truncada em uma fronteira de/. O casamento é puramente lexical: ele nunca consulta o sistema de arquivos e não faz distinção entre um caminho que nomeia um arquivo e um que nomeia um diretório, porque, depois da normalização, os dois são indistinguíveis. Isso é contenção em qualquer direção (a relaçãounderPathque as implementações de referência compartilham), de modo que uma tarefa de escopo amplo e um seletor declarado de forma estreita se encontram, qualquer que seja o lado mais amplo. - Casamento de categoria (estreito) e os dois conjuntos de categorias. As categorias de uma tarefa se dividem em expandidas e sinalizadas. Uma categoria que a tarefa nomeia explicitamente entra nos dois conjuntos. Um caminho de tarefa que é, ele próprio, um documento governado (casando com sua entrada de índice gerada por igualdade exata, nunca por contenção) contribui com a categoria dessa entrada apenas para o conjunto sinalizado. As categorias expandidas carregam seus documentos de intenção e seus candidatos a registro; as categorias sinalizadas são um sinal de casamento para decisões e mounts de federação e não carregam nada por conta própria. Um seletor de categoria MUST NOT inferir uma categoria para um arquivo qualquer do repositório, e um caminho de tarefa que não seja, ele próprio, um documento governado, incluindo qualquer diretório ancestral de um, não contribui com categoria nenhuma. O escopo de caminho alcança arquivos; a expansão de categoria não o segue.
- Casamento de tópico (exato, apenas para mounts). Um tópico é uma string não vazia de valores escalares Unicode, comparada por sua codificação UTF-8; um surrogate isolado não é um tópico válido. Os dois lados estão sujeitos a essa regra: um tópico de tarefa ou uma entrada de
topicsde um mount que não seja uma string não vazia de valores escalares Unicode é um erro de entrada, e uma implementação MUST rejeitá-lo em vez de devolvê-lo como um não casamento silencioso. Um tópico de tarefa casa com um tópico declarado quando as duas strings decodificadas são exatamente iguais. As implementações MUST NOT converter maiúsculas e minúsculas, normalizar Unicode, comparar por localidade, aparar, tokenizar, casar por substring nem casar de forma aproximada nenhum dos lados, de modo que grafias canonicamente equivalentes que difiram em bytes não casam; esta regra de igualdade é separada da ordenação por bytes dos resultados, mais abaixo. Tópicos de tarefa duplicados formam um único sinal, então nomear um tópico duas vezes casa exatamente como nomeá-lo uma vez. Um mount de federação casa quando qualquer tópico da tarefa é igual a qualquer tópico que o mount declara. Um casamento de tópico seleciona apenas o mount: ele MUST NOT entrar nos conjuntos de categorias expandidas ou sinalizadas, carregar documento ou registro algum, rotear decisão alguma, avaliarrequiredWhennem tornar um mount obrigatório. - Filtro de status. Só um registro de decisão cujo
statusvincula é roteado como orientação atual.acceptededeprecatedvinculam; um registrodeprecatedvincula com postura de desatualizado, e um agente MUST tratá-lo como orientação a caminho da saída, e não como prática atual assentada. Um registrosupersededMUST NOT vincular a não ser como histórico e MUST carregarsupersededBy; registrosproposederejectedMUST NOT vincular. Um registro que vincula é vivo. - Decisões sem escopo. Um registro de decisão vivo que não declara nem
affectedPathsnemaffectedCategoriesvale para a organização inteira: ele é roteado para toda tarefa, qualquer que seja o escopo dela. Decisões vivas com escopo são roteadas apenas quando a tarefa casa com elas por caminho (2) ou por categoria (3). - Escopo de caminho vazio e escopo vazio. Quando o conjunto de caminhos da tarefa está vazio, o casamento de caminho não contribui com nada e um agente MUST declarar que o roteamento por escopo de caminho não foi avaliado; as categorias nomeadas explicitamente continuam sendo honradas e continuam expandindo, e os tópicos nomeados explicitamente continuam sendo casados. Categorias nomeadas e tópicos nomeados contam ambos para um escopo não vazio. O escopo inteiro da tarefa está vazio somente quando ela não nomeia caminhos, nem categorias, nem tópicos; nesse caso, um agente roteia apenas o contexto incondicional do perfil de boot e do perfil de agente, mais as decisões vivas sem escopo que valem para a organização inteira. Um agente MUST NOT apresentar uma carga não roteada como se ela tivesse escopo.
- Registros são roteados como candidatos. Uma categoria expandida roteia seus documentos de intenção como contexto obrigatório; os registros dessa categoria são devolvidos à parte, cada um com seu tipo e sua data, como candidatos que o leitor carrega por julgamento. Um registro se torna obrigatório apenas quando os caminhos da tarefa o selecionam diretamente pelo item 2, quando um agente ou perfil de boot o nomeia, ou quando uma pessoa o pede; ser um casamento de categoria, ou carregar a data mais recente, nunca torna um registro obrigatório. Onde um registro é ao mesmo tempo candidato por categoria e selecionado diretamente por caminho, a seleção direta vence e ele é obrigatório. O roteamento MUST NOT certificar nenhum registro como “o mais recente” ou “o atual”: a 1.0 não define identidade de série de registros nem garantia de ordenação, então os juízos de recência pertencem ao leitor, feitos contra as datas que o índice expõe. Os registros de decisão mantêm o roteamento próprio (itens 5 e 6) e nunca são roteados como registros genéricos. Nomear um arquivo de decisão como caminho de tarefa não roteia aquela decisão; quem roteia é o escopo que ela declara.
- Citação. Um agente que carrega registros de decisão roteados MUST citar quais registros casados ele carregou, para que um leitor possa ver que orientação o agente aplicou e inferir a que ele não aplicou.
A fatia roteada é o que um agente carrega para uma tarefa. Ela é a união de: o conjunto de carregamento incondicional do perfil de boot e o requiredRead do perfil de agente ativo, que o agente sustenta como sua linha de base independentemente de qualquer escopo; todo documento de intenção governado em uma categoria expandida; toda entrada governada que os caminhos da tarefa selecionam pelo item 2; todo registro selecionado diretamente por caminho pelo item 8; e toda decisão viva com a qual a tarefa casa por caminho ou por uma categoria sinalizada ou expandida, mais as decisões vivas sem escopo que valem para a organização inteira.
As categorias sinalizadas contribuem apenas com casamento, para decisões e para mounts de federação, e nunca expandem um corpus.
A fatia não é o envelope. Uma ferramenta que calcula o roteamento devolve a fatia junto com material que o agente não deve carregar sem ser solicitado: os candidatos a registro e os metadados de candidatura roteada em torno deles. Carregar o envelope inteiro derrota o propósito do roteamento.
A ordenação dos resultados é normativa onde uma ferramenta emite uma, para que implementações independentes concordem byte a byte: as categorias na ordem canônica de categorias desta especificação; documentos, registros e decisões por caminho ascendente; mounts por nome ascendente. A comparação de strings é byte a byte sobre UTF-8, não dependente de localidade nem de colação por ponto de código.
O ferramental MAY oferecer um auxiliar que calcule essa fatia a partir de um conjunto de caminhos; as implementações de referência expõem um (route). Esse auxiliar calcula a porção dependente do escopo e não é obrigado a emitir a linha de base, que quem chama já sustenta; a obrigação do agente de carregar essa linha de base permanece inalterada. O roteamento é conforme sempre que um leitor sem ferramental segue este algoritmo.
Notas (não normativo)#
O ferramental de referência hoje verifica o schema do changelog e a disciplina de somente acréscimo (leji validate roda os dois); verificar a cobertura do changelog contra uma revisão base, ou seja, que todo caminho governado alterado apareça em uma entrada acrescentada, é uma verificação reportada no roteiro, ainda não uma verificação bloqueante. Até isso chegar, a cobertura se apoia na disciplina atestada por processo de revisão e CI (ver conformance.md).
O índice é a fonte de navegação do contexto governado: leji viewer renderiza essa espinha governada a partir do índice e, abaixo dela, a própria árvore de diretórios do repositório como uma zona de referência navegável, de modo que uma única visão expõe tanto o contexto governado quanto a navegação já existente da equipe. Qualquer ferramenta de documentação pode projetar o índice do mesmo jeito. A apresentação não é normativa. A superfície é deliberadamente pequena. Cinco formatos bastam para que o ferramental valide uma camada de contexto, produza diffs dela, pontue sua atualidade e roteie um agente até a fatia certa, e são poucos o bastante para que uma equipe consiga guardar a superfície inteira na cabeça. Qualquer coisa além destes cinco é território pós-1.0, condicionado à prática vivida.