spec 1.0 · normativo
A Especificação Leji
A especificação normativa completa em uma única página, organizada na ordem de leitura para quem quiser percorrê-la de ponta a ponta ou fazer uma pesquisa. Cada seção leva à página correspondente, onde todos os títulos incluem uma âncora de citação.
A Especificação Leji
Leji é uma especificação aberta para a camada de contexto compartilhada de equipes nativas de IA. Ela define como uma equipe armazena, governa, carrega e mantém o contexto pertencente ao repositório, consultado por pessoas e agentes de IA a cada tarefa.
| Versão da especificação | 1.0.0 |
| Status | GA, congelada na versão publicada v1.3.0 do ferramental de referência. Mudanças incompatíveis exigem uma nova versão maior. |
| Editor | Vuong Nguyen |
| Página única | A especificação completa em uma única página |
Princípios (não normativo)#
- Intenção acima de instruções. Leji registra a intenção duradoura (o que as coisas significam, o que precisa valer e por que é assim) em vez de instruções imperativas específicas de cada fornecedor. Pessoas e agentes definem suas ações com base na intenção declarada e no contexto da tarefa.
- Um círculo, não um degrau. Humano para humano, humano para IA e humano para IA para humano são fluxos de primeira classe em torno de uma única camada de contexto compartilhada. Acesso igual, não autoridade igual: todo mundo com acesso a uma camada de contexto lê tudo, qualquer um propõe, pessoas aprovam. A participação é definida pelo papel, não pela ferramenta: quem nunca toca no git diretamente é participante de primeira classe no círculo. Conceder o acesso em si cabe ao sistema de controle de versão, não ao Leji; o círculo tem como escopo o público de uma camada de contexto.
- Mecanismo acima de boa vontade. Por padrão, o contexto compartilhado se deteriora: a realidade muda, os documentos não, e nada obriga um wiki a permanecer atualizado. Os mecanismos de imposição do Leji são mecânicos, não dependem de boa vontade: as mudanças passam pelo mesmo processo de revisão e aprovação que o código, as ferramentas falham diante de desvios mecânicos, os horizontes de atualidade sinalizam o que envelheceu, e o contexto desatualizado nunca é tratado silenciosamente como atual (normativamente, governance.md → Atualidade).
O restante desta especificação é a consequência normativa desses três princípios.
Linguagem de conformidade#
As palavras-chave MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, RECOMMENDED, MAY e OPTIONAL nesta especificação devem ser interpretadas conforme descrito na RFC 2119.
Como citar esta especificação (não normativo)#
Cite uma seção pelo título e pela versão da especificação, com um link permanente para a âncora da seção. No site da especificação, cada título revela sua âncora ao passar o cursor.
- Formato: Leji 1.0, §Seção:
https://leji.org/spec/<document>/#<anchor> - Exemplo: Leji 1.0, §The circle, normatively:
https://leji.org/spec/governance/#the-circle-normatively
Cite sempre a versão (Leji 1.0): mudanças incompatíveis são publicadas como uma nova versão maior, então uma citação presa a uma versão continua exata depois que a especificação evolui.
Vocabulário#
Estes termos são usados de forma consistente em todos os documentos normativos:
| Termo | Significado |
|---|---|
| camada de contexto (context layer) | O artefato que esta especificação governa: um conjunto versionado de documentos legíveis por humanos e artefatos legíveis por máquina, pertencente ao repositório, que codifica o contexto operacional duradouro de uma equipe. “Camada de contexto Leji” é a forma completa e desambiguadora. Escreva sempre “camada de contexto”; o termo isolado “camada” fica reservado para nomear uma instância contável de federação (uma camada de contexto irmã, hospedeira, montada, restrita, complementar ou inacessível). |
| agente (agent) | Um sistema de IA que age: carrega o contexto do repositório, executa ou apoia o trabalho e pode propor mudanças. É o substantivo normativo para o ator. |
| pessoa / pessoas | Participantes humanos. As pessoas detêm a autoridade de aprovação. |
| participante | Uma pessoa ou um agente. |
| público (audience) | As pessoas e os agentes admitidos a ler uma camada de contexto pelas permissões do repositório e por quaisquer permissões de sistema de arquivos ou de disco compartilhado que exponham o checkout. “Todo mundo lê” tem como escopo o público de uma camada de contexto; públicos diferentes são atendidos por camadas de contexto separadas, nunca por restrição de conteúdo dentro de uma delas. |
| host de agente (agent host) | O produto ou runtime através do qual um agente opera (por exemplo Claude Code, Codex, Cursor). Os adaptadores de fornecedor configuram hosts de agente. |
| ferramenta (tool) | Uma capacidade invocável que um agente usa (shell, busca, um servidor MCP). Nunca um nome de produto. |
| adaptador de fornecedor (vendor adapter) | Um arquivo de entrada de um host de agente que redireciona para o perfil de boot e nunca guarda conteúdo canônico. Alguns são portáveis entre hosts (AGENTS.md); outros servem a um único host (CLAUDE.md, .cursor/rules). A regra é a mesma para ambos; a diferença muda apenas o que o ferramental gera por padrão. |
| perfil de boot (boot profile) | O ponto de entrada agnóstico de agente da camada de contexto, tanto para pessoas quanto para agentes. |
| perfil de agente (agent profile) | Um documento de carregamento e postura específico de um papel, dirigido a agentes. |
| IA (AI) | Usado como adjetivo (nativo de IA) e nos nomes de fluxo humano para humano, humano para IA, humano para IA para humano. Nos nomes de fluxo, “IA” se refere a agentes operando através de um host de agente. |
| modelo (model) | O motor preditivo sobre o qual um agente roda. Modelos não leem a camada de contexto; agentes leem. Aparece apenas onde o motor precisa ser distinguido do ator (por exemplo, a escolha de modelo como mecânica específica do host). |
A hierarquia, em uma linha: um modelo move um agente; um agente opera através de um host de agente e chama ferramentas; a camada de contexto se dirige a agentes e hosts, nunca diretamente a modelos. A especificação é agnóstica em todos os níveis dessa pilha: qualquer modelo, movendo qualquer agente, operando através de qualquer host, lendo a mesma camada de contexto. “LLM” foi deliberadamente deixado fora deste vocabulário: nomeia uma classe de modelo, e a especificação é agnóstica de modelo pelo mesmo princípio.
Limite de escopo. Leji 1.0 governa agentes e os hosts de agente que carregam contexto de repositório. IA não agêntica (autocompletar, sugestões em linha, chat sem contexto do repositório) está fora do escopo normativo, exceto quando opera como parte de um host de agente que carrega a camada de contexto.
Documentos normativos#
Em ordem de leitura:
| Documento | Define |
|---|---|
| context-layer.md | A camada de contexto, o manifesto, a raiz, a regra do adaptador de fornecedor |
| content-categories.md | As cinco categorias lógicas de conteúdo e como os arquivos de índice mapeiam conteúdo para elas |
| boot-profile.md | O ponto de entrada agnóstico de agente que todo host de agente carrega |
| machine-readable-surface.md | Manifesto, índice, changelog, perfis, registros de decisão |
| decisions.md | Registros de decisão |
| governance.md | Propor/aprovar, propriedade, inclusão e remoção, atualidade |
| distribution.md | Monorepo, submódulo multirrepo, federação |
| conformance.md | Os quatro níveis de conformidade e o checklist |
| versioning.md | Versionamento da especificação e dos schemas |
Os JSON Schemas em ../schemas/ são normativos para os artefatos legíveis por máquina. Os documentos em ../rationale/ e ../adoption/ não são normativos.
Escopo da 1.0#
No escopo: fornecer contexto, estabelecer restrições, registrar decisões, revisar mudanças e capturar padrões reutilizáveis; ligação agnóstica de agente e adaptadores de fornecedor (de forma leve); semântica de propriedade e continuidade (de forma leve).
Limite de extensão. Leji 1.0 especifica a camada de contexto compartilhada canônica: como o contexto de uma equipe é escrito, possuído, versionado, proposto, aprovado, indexado e lido. Ela deliberadamente não especifica os protocolos de execução que operam ao redor dessa camada de contexto: envelopes de tarefa, um protocolo generalizado de evidência, repasse entre agentes, protocolos de permissão de ferramentas e orquestração. Esses são protocolos de extensão, não pré-requisitos: uma camada de contexto conforme com a 1.0 MUST continuar útil sem eles, e uma implementação MUST NOT exigi-los para ler, propor, revisar, aprovar ou validar a camada de contexto. Eles completam a linguagem à medida que a prática real os comprova; não são inventados no abstrato.
Leji não é uma linguagem de programação, uma DSL, um runtime ou um SaaS. É um conjunto de convenções em markdown, pequenos JSON Schemas e semântica de governança.
A Camada de Contexto
Uma camada de contexto Leji é um conjunto versionado e governado de documentos legíveis por humanos que registra como uma equipe pensa sobre o próprio trabalho: linguagem do domínio, invariantes do sistema, convenções, travas de proteção e registros de decisão. Pessoas e agentes a consultam durante o trabalho e propõem mudanças pelo mesmo processo de revisão e aprovação. Ela fica sob controle de versão para que o histórico, a atualidade e a aprovação continuem verificáveis; a mecânica está em Requisitos, abaixo, e as formas de participação estão em Participação.
Participação#
A participação em uma camada de contexto é definida pelo papel, não pela ferramenta. A leitura, a proposta, a revisão e a aprovação podem acontecer em qualquer interface que preserve a semântica de revisão e aprovação do repositório; não é preciso conhecer git ou linha de comando diretamente para participar.
- Todo mundo com acesso lê. Acesso significa acesso prático pelas ferramentas normais da equipe, não acesso a shell no repositório.
- Qualquer um propõe; pessoas aprovam. Uma proposta é um pedido intencional de mudança na camada de contexto. Ela MAY ser escrita diretamente por uma pessoa, gerada por um agente a partir do pedido de uma pessoa, ou gerada por um agente a partir do trabalho observado. Na prática, os agentes redigem a maior parte das mudanças de contexto; a contribuição humana irredutível é a governança: propor a intenção e aprovar o que se torna canônico. A pessoa que aprova uma mudança responde pelo significado e pela consequência dela, não por operar pessoalmente o sistema de controle de versão.
- Significado humano, superfície legível por máquina. Os documentos legíveis por humanos são a fonte normativa do contexto operacional de uma equipe. Os arquivos legíveis por máquina (manifesto, índice, changelog) existem para que as ferramentas possam localizar, indexar, validar e sincronizar esse significado; eles nunca o substituem.
A forma normativa desses fluxos, o círculo (todo mundo lê, qualquer um propõe, pessoas aprovam), está definida em governance.md.
Lendo um registro#
O conteúdo governado vem em dois tipos, definidos em content-categories.md: intenção, mantida como verdade presente, e registros, evidência datada que o estado posterior supera em vez de corrigir. Ambos são igualmente governados; o que difere é o que um leitor pode fazer com o que carregou. Um leitor MUST NOT tratar um registro como intenção atual: um registro informa com o peso de evidência verdadeira dentro do limite que declara, e um leitor que apresenta as afirmações de um registro como o estado presente das coisas, sem dizê-lo, está fabricando uma atualidade que o documento não tem. Isso espelha a regra do modo degradado, abaixo: nos dois casos, a obrigação do leitor é saber, e dizer, que tipo de atualidade está segurando.
Requisitos#
-
A camada de contexto MUST viver em um repositório git e MUST ser versionada junto com o trabalho que descreve (o mesmo repositório, ou um repositório de contexto dedicado consumido conforme distribution.md). O repositório git é o que torna verificáveis o histórico da camada de contexto, a atualidade do checkout e a integridade somente acréscimo do changelog; o ferramental conforme deriva os três dele. Ler a camada de contexto sem esse repositório é um modo suportado, porém degradado, definido em Modos de leitura.
-
Um repositório que adota Leji MUST ter um arquivo de manifesto,
leji.json, na raiz do repositório, válido segundocontext-manifest.schema.json. O manifesto é o ponto de entrada da máquina: ele declara a versão da especificação (a chave autonomeadoraleji), o nome da camada de contexto, a raiz do contexto, o caminho do perfil de boot, os mapeamentos de categoria, uma declaração opcional de conformidade e a propriedade. Ele MAY carregar também um mapaagentsque liga identificadores de papel (por exemplothought-partner,reviewer) a documentos de perfil de agente: os protocolos engajam papéis; o mapa decide quem os preenche. O mapa é um diretório de papéis, não uma ordem de carregamento: uma ligação, inclusive uma na chavedefault, nunca faz um perfil ser lido; só a seção Carregamento do perfil de boot faz isso. -
O manifesto MAY declarar atores: participantes nomeados que podem preencher papéis. Cada ator declara os papéis para os quais é elegível e um modelo de comando por papel. Chavear o comando pelo papel é justamente o ponto: um mesmo ator pode exigir uma invocação diferente conforme o papel que está preenchendo, então um único comando por ator não daria conta de expressar isso. Os papéis declarados de um ator e as chaves de seus comandos MUST ser o mesmo conjunto. Onde um papel tem atores, o perfil de agente a ele ligado MUST NOT declarar também
invocation: dois comandos autoritativos sem precedência declarada são uma contradição, e a camada a resolve declarando o comando em um único lugar. Atores são opcionais e a maioria das camadas não precisa de nenhum. Eles se justificam quando um papel tem mais de um ator elegível, ou quando um ator precisa de uma invocação diferente conforme o papel que preenche; qualquer um dos dois já basta, e um papel cujo único ator precisa de um só comando é bem servido pelohoste peloinvocationdo próprio perfil. Declarar um ator não concede autoridade nenhuma: diz quem pode ser chamado a preencher um papel, nunca quem pode aprovar.Modelos de comando, onde quer que apareçam (os valores de
commandsde um ator e oinvocation.commandde um perfil de agente), seguem uma única regra. Um modelo é uma linha de comando para o shell que o invocador escolher; um engajamento que não tenha formato de shell (uma chamada estruturada de argv, um spawn dentro do processo) não é representável por esses campos na linha 1.0. Todo modelo MUST conter o marcador<prompt>, e cada ocorrência MUST figurar como uma palavra de shell própria e sem aspas, em posição de argumento, nunca dentro de aspas nem colada a outro texto. A substituição é de passada única: as ocorrências presentes no modelo escrito são substituídas simultaneamente, exatamente uma vez, de modo que a string literal<prompt>dentro do texto do prompt permaneça dado e nunca seja reexpandida. A entrega pertence ao invocador, e o contrato é o resultado exigido, não o algoritmo de aspas: cada ocorrência produz exatamente um argumento cujo valor é igual ao texto do prompt, sem que nada dele seja avaliado como sintaxe de shell. O que os schemas verificam é a presença de um ponto de substituição; posicionamento e entrega cabem a esta regra exigir e ao invocador honrar. -
O manifesto MUST declarar uma raiz do contexto (
rootPath). O padrão RECOMMENDED édocs/. Todos os caminhos da camada de contexto são no estilo POSIX, relativos à raiz do repositório.rootPathdeclara onde a camada de contexto vive; ele não rebaseia os caminhos que governa: as entradas do índice, as páginas fixadas, os caminhos dos perfis e todo outro caminho em todo artefato Leji resolvem a partir da raiz do repositório, inclusive os que repetem o prefixo derootPath. Ohomepage, ologoe ofavicondo visualizador são a exceção: eles são escritos relativos à raiz do contexto, e um caminho relativo à raiz do repositório que fique sob ela também é aceito. Os caminhos de categoria e demachineSHOULD ficar sobrootPath; os validadores avisam quando não ficam. -
A camada de contexto MUST ter um perfil de boot conforme boot-profile.md. O local padrão RECOMMENDED é
docs/boot-profile.md; obootProfilePathdo manifesto declara o local efetivo. -
O conteúdo da camada de contexto MUST ser legível por humanos antes de tudo. Markdown é o formato RECOMMENDED para prosa; metadados estruturados usam frontmatter YAML ou os artefatos JSON definidos em machine-readable-surface.md. Um documento que só uma máquina consegue ler não pertence à camada de contexto.
-
A camada de contexto MUST ter um dono nomeado (
owners.primaryno manifesto): uma pessoa responsável pela atualidade dela. Camadas de contexto sem dono apodrecem.
Modos de leitura: canônico e degradado#
Uma camada de contexto é lida em dois modos; um leitor MUST saber em qual modo está, porque as garantias são diferentes. Um leitor determina seu modo a partir do que consegue resolver: é canônico quando há um leji.json alcançável na raiz do repositório e, além dele, pelo menos um entre uma árvore de trabalho git e a identidade de revisão do repositório na plataforma hospedeira; conteúdo alcançado como arquivos simples sem nenhum dos dois é degradado.
- Canônico. O leitor resolve a camada de contexto através do seu repositório git: um checkout, ou a visão do repositório na plataforma hospedeira. Histórico, atualidade do checkout e integridade do changelog são verificáveis, e o conteúdo aprovado é sabidamente atual até a revisão lida.
- Degradado. O leitor alcança a camada de contexto como conteúdo de arquivo simples, sem árvore de trabalho git acessível nem metadados de versão: arquivos enviados, sincronizados ou copiados para outra interface sem o repositório. A leitura de arquivos simples é de primeira classe para leitura (os documentos são legíveis por humanos por exigência, e o changelog legível por máquina ainda transmite a recência declarada), mas um leitor em modo degradado MUST tratar a atualidade do checkout e o estado de aprovação como desconhecidos, nunca como atuais (ver governance.md, Atualidade). O changelog é a superfície portátil de recência declarada nesse modo; ele não estabelece, por si só, que a cópia corresponde ao repositório canônico.
A leitura degradada amplia quem e o que pode consumir uma camada de contexto; ela nunca é um caminho para a autoridade canônica. Uma mudança só se torna canônica através do processo de revisão e aprovação apoiado em git, e uma cópia degradada não consegue satisfazer as verificações de modo canônico das quais a federação depende (atualidade do pin, estado de pin desatualizado, integridade da propriedade e acesso a mount restrito, conforme distribution.md).
A regra do adaptador de fornecedor#
Arquivos de configuração de host de agente (por exemplo CLAUDE.md, AGENTS.md, GEMINI.md, .cursorrules, .cursor/rules, .windsurfrules, .github/copilot-instructions.md):
- MUST NOT guardar conteúdo canônico da camada de contexto.
- Se presentes, MUST redirecionar para o perfil de boot (tipicamente um ponteiro de uma linha).
- MAY carregar mecânicas específicas do host que não têm significado fora daquele host de agente (escolha de modelo, configurações do executor), desde que nenhum conhecimento da equipe viva ali.
- O ferramental descobre quais pontos de entrada verificar a partir de duas fontes: a lista opcional
vendorAdaptersdo manifesto e um conjunto conhecido e publicado (os arquivos nomeados acima). A lista de exemplo acima é esse conjunto conhecido para esta linha; um host cujo ponto de entrada não esteja nela só é verificado quando o manifesto o nomeia emvendorAdapters.
As convenções de ponto de entrada que existem dizem a um host de agente onde olhar; Leji define o que o agente encontra ali. Uma única fonte de verdade, com todos os participantes lendo dela.
O que a camada de contexto não é (não normativo)#
- Não é um wiki. Nada obriga um wiki a se manter atual. A camada de contexto continua viva porque os agentes a leem a cada tarefa (contexto errado produz saída errada, e isso se sente na hora), as mudanças passam pela revisão como o código, e o ferramental torna a desatualização visível.
- Não é documentação no sentido tradicional. A documentação descreve o que o sistema faz, depois do fato. A camada de contexto descreve como a equipe pensa, no presente, e é lida o tempo todo por pessoas e agentes.
- Não é um template para importar. Contexto emprestado fica desatualizado de imediato. O valor de uma camada de contexto é ser a representação da própria equipe; Leji padroniza o formato e a governança, não o conteúdo.
Categorias de Conteúdo
Leji define cinco categorias lógicas de conteúdo. Elas classificam a finalidade de um documento, não o local onde ele fica: os nomes das categorias são identificadores estáveis usados pelo manifesto, pelo índice e pelas ferramentas. Os nomes dos diretórios ficam a critério da equipe.
As cinco categorias#
| Categoria | O que pertence a ela |
|---|---|
domain |
Linguagem de negócio e semântica de produto, nas palavras da própria equipe: o que significam os substantivos centrais, como eles se relacionam, os termos com sentido local. Registros de estado de negócio (o status de um engajamento, um retrato de mercado) também se classificam aqui, como registros. |
system |
Arquitetura e seus invariantes: fronteiras de serviço, propriedade de dados, contratos de integração, modelos de consistência, contratos de falha, as restrições com as quais toda mudança convive. Avaliações técnicas e panoramas de sistema se classificam aqui, como registros. |
practice |
Convenções e padrões aplicados automaticamente: convenções de código, padrões de teste e os padrões de prompt e de fluxo de trabalho que já se provaram (ver o critério de inclusão abaixo). Registros da aplicação de um método (uma retrospectiva, o log de execução de um runbook) se classificam aqui, como registros. |
governance |
Travas de proteção de agentes e regras operacionais: o que os agentes podem fazer sem serem solicitados, o que exige aprovação humana, regras de tratamento de dados, gatilhos de escalonamento, controles de conformidade regulatória. Evidência de governança (um log de auditoria, um relatório de revisão) se classifica aqui, como registros. |
decisions |
Registros datados do porquê de as coisas serem como são, conforme decisions.md. |
Intenção e registros#
Todo documento governado é ou intenção ou um registro, independentemente de sua categoria:
- Intenção é verdade presente mantida: glossários, invariantes, convenções, travas de proteção. Os leitores contam com ela como atual, então, quando a realidade muda, o documento é corrigido. A intenção é a razão de existirem os horizontes de revisão e o mecanismo de atualidade (ver governance.md).
- Um registro preserva afirmações dentro de um limite temporal ou de evento explícito: status, avaliações, livros-razão, panoramas, resultados de reunião, arquivos. O estado posterior supera um registro em vez de corrigi-lo; o original continua sendo um relato válido do seu tempo. A superfície de atualidade de um registro é sua data, nunca um horizonte de revisão.
Para classificar, basta fazer uma pergunta: se uma informação posterior contradisser este documento, será preciso corrigi-lo porque os leitores contam com ele como atual, ou a nova informação o substituirá enquanto o original permanece um relato válido de seu tempo? Se precisa ser corrigido, é intenção; se for substituído, é registro.
Um registro é governado exatamente como a intenção: indexado, revisado, com dono e roteado. O que difere é o que um leitor pode fazer com ele: um leitor MUST NOT tratar um registro como intenção atual; ele é evidência datada (ver context-layer.md, Lendo um registro). Os registros de decisão são o subtipo formal de registro: são registros por natureza, com schema e ciclo de vida próprios conforme decisions.md.
Algumas perguntas sobre registros ficaram deliberadamente fora da 1.0 e são reconhecidas em vez de escondidas: não existe noção de máquina para uma série de registros (então o ferramental nunca certifica qual registro é “o mais recente”), não existe mecanismo de recência de fluxo (se o próximo registro esperado está atrasado) e não existem tipos por seção para documentos que misturem materialmente conteúdo de intenção e de registro. Um documento misto SHOULD ser dividido; onde dividir for desproporcional, classifique pelo contrato do qual os leitores a jusante principalmente dependem. Conteúdo que honestamente não cabe em nenhuma categoria permanece como referência; a classificação não promete ser livre de julgamento.
Requisitos#
- O manifesto MUST mapear cada categoria que declara para um ou mais arquivos de índice relativos à raiz do repositório (
categories.<id>.indexes); cada arquivo de índice SHOULD ficar sob a raiz do contexto declarada, conforme context-layer.md. Um arquivo de índice declara inclusão, ele não realoca: o conteúdo permanece onde a equipe já o guarda (por exemplobusiness/,technology/,architecture/), e um mesmo diretório pode contribuir com documentos para mais de uma categoria sem renomear nada. - Um arquivo de índice é markdown curado que carrega um ou mais blocos de código cercados com a marca
leji-index. Um bloco abre com uma linha de três ou mais crases seguida da info string do bloco e fecha com a linha seguinte de três ou mais crases; a quantidade de crases da cerca de fechamento não precisa coincidir com a de abertura. Exatamente três info strings são válidas:leji-index(um bloco de intenção),leji-index intent(o mesmo, de forma explícita) eleji-index record(um bloco de registros, cujas entradas resolvem como registros). Qualquer outro token depois deleji-indexé um erro de parsing, nunca ignorado em silêncio: a gramática é finita por projeto. Cada bloco lista conteúdo, uma entrada por linha, como- path: <repository-root-relative-path>, onde um caminho é um diretório (seu markdown é incluído recursivamente) ou um único arquivo markdown. Um caminho MUST ser POSIX relativo à raiz do repositório: uma/inicial, um segmento..ou uma barra invertida é inválido e rejeitado. Linhas em branco e comentários#de linha inteira são ignorados, e uma entrada MAY trazer ao final um# commentprecedido de espaço em branco. Espaço em branco nesta gramática é o espaço ASCII (U+0020) e a tabulação (U+0009) e mais nada, em todo lugar em que a gramática o consulta: em torno das crases da cerca e da info string, como preenchimento inicial e final em uma linha de entrada, e antes do#que abre um comentário final. Uma marca de ordem de bytes UTF-8 inicial é removida antes do parsing. As linhas são separadas por LF, com um CR final tolerado, e o arquivo é UTF-8. As implementações MUST NOT usar uma classe de espaço em branco do runtime aqui: qualquer outro caractere que um runtime por acaso classifique como espaço em branco, entre eles U+0085 e U+00A0, é conteúdo comum de caminho, de modo que uma entrada cujo caminho carregue um deles é reportada como ausente em vez de ser silenciosamente aparada. Os blocosleji-mountsde boot-profile.md estão congelados sobre o mesmo alfabeto, de modo que um único scanner lê as duas gramáticas e três implementações não podem discordar sobre se uma cerca sequer existe. Vários blocos em um mesmo arquivo são concatenados na ordem do documento. Prosa e títulos ao redor dos blocos são permitidos, então um arquivo de índice serve também como mapa legível por humanos da categoria. A varredura é feita por linha e não consulta a estrutura do markdown: uma linha que carregue três ou mais crases e a marca, depois de recuo opcional de espaço ou tabulação, abre um bloco real onde quer que esteja no documento, inclusive dentro de um exemplo cercado mais longo ou dentro de um item de lista. Um exemplo destinado a ilustrar em vez de declarar é, portanto, cercado com uma marca diferente, nunca com um token extra depois deleji-index: a marca é aquilo que o scanner reconhece, entãoleji-index exampleabre um bloco real e reporta um erro de parsing, enquanto uma cerca marcada comotextnão abre nada. O local RECOMMENDED écontext/<id>.mdsob a raiz do contexto; o local é configurável e o ferramental nunca o fixa no código. - Uma camada de contexto MUST mapear ao menos
domainousystem, maisdecisions, para declarar qualquer nível de conformidade (ver conformance.md), e o mínimo populado dedomain/systemMUST incluir ao menos um documento de intenção: uma camada de contexto feita só de registros preserva histórico, mas não carrega contexto operacional. As demais categorias vão se acumulando conforme a equipe esbarra em perguntas reais; uma categoria vazia (aquela cujos arquivos de índice não resolvem para documento nenhum) MUST NOT ser mapeada para satisfazer um checklist. - Um documento resolve para exatamente uma categoria e um tipo. As entradas de índice são seletores, e a resolução segue a especificidade do seletor: um seletor de arquivo direto vence qualquer seletor de diretório, e um seletor de diretório mais profundo vence um seletor de diretório ancestral. O seletor mais específico que cobre um documento determina sua categoria e o tipo do seu bloco; um documento que um seletor mais amplo cobre, mas que um seletor mais específico vence, simplesmente não é conteúdo daquele seletor mais amplo (que é como se expressa um arquivo mantido atual dentro de um diretório de registros, ou o log de decisões de uma equipe dentro de uma árvore mapeada mais ampla, sem mover nada). Seletores de especificidade igual que discordem quanto à categoria ou ao tipo são um erro, nunca resolvido pela ordem do índice; atribuições idênticas de igual especificidade resolvem uma única vez, enquanto uma entrada literalmente duplicada dentro de um mesmo arquivo de índice é rejeitada. O ferramental SHOULD expor um seletor cujos documentos cobertos foram todos vencidos por seletores mais específicos (um seletor sombreado): peso morto no mapa curado, nunca um erro. Fora isso, a resolução é determinística: uma entrada de diretório se expande para o markdown dela em ordem lexicográfica POSIX (por ponto de código Unicode; RECOMMENDED que os caminhos permaneçam em ASCII para que a ordem não seja ambígua entre implementações), e qualquer caminho cuja localização real (depois de resolver links simbólicos) escape da raiz do repositório é excluído em vez de seguido. As entradas de índice (ver machine-readable-surface.md) carregam o identificador da categoria e o tipo.
- Um documento MAY declarar seu tipo no frontmatter (
kind: intentoukind: record); o frontmatter sobrepõe o tipo de bloco do seletor vencedor e nunca a categoria. Qualquer outro valor dekindé um erro. Registros de decisão não aceitam a chavekind(o schema deles é fechado e eles são registros por natureza). Um registro MAY trazer umadateno frontmatter (YYYY-MM-DD); o ferramental lê a data de um registro somente desse campo, nunca da prosa, de convenções de cabeçalho ou de nomes de arquivo. Um registro MUST NOT carregarfreshness.reviewAfter(um horizonte de revisão é um mecanismo de intenção; em um registro, ele promete uma atualidade que o documento não pode ter, e isso é um erro). - Conteúdo de prática que descreve padrões de prompt ou de fluxo de trabalho SHOULD ser capturado somente depois que o padrão tiver funcionado ao menos duas vezes (o critério de comprovação em duas tarefas). A captura prematura é como os diretórios de prática se enchem de aspiração.
Notas (não normativo)#
Nem toda categoria está presente no primeiro dia. A camada de contexto mínima viável é aquilo de que o primeiro mês de trabalho realmente depende. As categorias existem para que uma pessoa ou um agente possa perguntar “que tipo de verdade é esta?” e carregar a fatia que importa para a tarefa em mãos, em vez da árvore inteira.
Os dois tipos existem porque a documentação de um repositório real é formada por dois corpora entrelaçados com modelos de verdade diferentes, e forçar a metade operacional sob a semântica de intenção falha dos dois lados: promessas de atualidade que não podem ser honradas, ou a maior parte do repositório exilada fora da governança. Um exemplo completo do formato, com uma exceção de intenção dentro de um diretório de registros:
# Contexto de domínio
```leji-index
- path: docs/glossary.md
```
O estado operacional é governado como registros; a política de escalonamento permanece intenção.
```leji-index record
- path: docs/operations/
```
```leji-index intent
- path: docs/operations/escalation-policy.md
```
O Perfil de Boot
O perfil de boot é o ponto de entrada da camada de contexto, independentemente do agente: um único documento legível por humanos que serve de ponto de partida para qualquer pessoa ou host de agente. Ele responde às perguntas “o que é esta camada de contexto, o que devo carregar e como devo me comportar aqui?”.
Requisitos#
-
A camada de contexto MUST ter exatamente um perfil de boot, localizado no caminho declarado por
bootProfilePathno manifesto. O padrão RECOMMENDED édocs/boot-profile.md. -
O perfil de boot MUST ser markdown puro, legível por uma pessoa sem ferramental nenhum. Ele MUST NOT depender da sintaxe de configuração de nenhum fornecedor.
-
O perfil de boot MUST cobrir:
- Identidade: o que é este repositório ou produto, em um parágrafo.
- Carregamento: que contexto ler para cada tipo de tarefa. Isso MUST apresentar um conjunto incondicional (o que ler antes de qualquer tarefa) e, em seguida, seletores por tipo de tarefa que roteiam por caminho, por categoria ou pelo índice de contexto, além de um fallback definido para uma tarefa que não case com seletor nenhum. Enunciado em linguagem de tarefa, isso é a expressão, no nível do perfil de boot, do algoritmo de Roteamento de tarefas (machine-readable-surface.md); segui-lo não exige conhecer esse algoritmo.
- Postura: as expectativas operacionais do agente (quando seguir em frente, quando perguntar, o que nunca fazer). Isso MAY ser trazido por referência a conteúdo de governança ou a um perfil de agente central.
-
O perfil de boot SHOULD apontar para o manifesto, o índice (se houver) e os perfis de agente (se houver), para que um agente que entre por qualquer host consiga descobrir toda a superfície legível por máquina.
-
O perfil de boot MUST falar em linguagem de tarefa: ele nomeia caminhos literais e uma ordem de carregamento concreta, e segui-lo não exige conhecer esta especificação. O manifesto e os schemas existem para o ferramental, não para os agentes; um perfil de boot que exija conhecimento da especificação para ser seguido é um mau sinal de conformidade.
-
O perfil de boot SHOULD enunciar os deveres de manutenção da camada de contexto: onde as mudanças dela são registradas (o changelog declarado) e como as decisões são capturadas (o local declarado dos registros de decisão). Os validadores avisam quando o perfil de boot não referencia nenhum dos dois.
-
Os arquivos de entrada de fornecedor redirecionam para o perfil de boot conforme a regra do adaptador de fornecedor em context-layer.md.
-
O conjunto de carregamento incondicional do perfil de boot (o que ele manda ler antes de qualquer tarefa) SHOULD se limitar ao que toda tarefa precisa. Contexto de que apenas algumas tarefas precisam SHOULD ser roteado por tarefa, categoria ou pelo índice, em vez de pré-carregado; e os registros de decisão SHOULD ser roteados pelos
affectedPaths/affectedCategoriesque declaram, em vez de carregados como um diretório inteiro, já que se acumulam sem limite. Tudo o que está no conjunto incondicional é pago em toda tarefa. -
Irmãs federadas. Uma camada de contexto que declara
federation.mounts(conforme distribution.md) MUST expor essas irmãs no perfil de boot em uma forma verificável por máquina: um ou mais blocos cercados cuja info string sejaleji-mounts, colocados em qualquer ponto do documento, cujas entradas se concatenam na ordem do documento e carregam exatamente uma entrada por mount declarado. Uma entrada nomeia a irmã, seu dono, o que ela carrega e quando lê-la, os dois últimos na linguagem de tarefa de quem escreve. O exemplo completo está abaixo dos requisitos.A gramática é fixa para que toda implementação a leia de forma idêntica. Um bloco abre com uma linha de três ou mais crases seguida da info string e fecha com a linha seguinte de três ou mais crases; a quantidade de crases da cerca de fechamento não precisa coincidir com a de abertura. A info string é
leji-mountssozinha; uma cerca que carregue qualquer token depois dela é um erro, nunca uma cerca ignorada. As linhas de cerca MAY carregar recuo e preenchimento de espaço ou tabulação, e os registros entre elas MUST NOT: um registro começa na coluna 1 com- mount:, e seus campos são recuados com exatamente dois espaços ASCII. Dentro de um registro,owner,carrieseread-whenaparecem exatamente uma vez cada, em qualquer ordem; campos desconhecidos, campos duplicados e campos ausentes são erros. Um valor é o restante não vazio da sua linha depois do prefixokey:, sem espaço ou tabulação no início ou no fim e sem caractere de controle ou separador de linha. Espaço em branco nesta gramática é o espaço ASCII (U+0020) e a tabulação (U+0009) e mais nada, tanto no recuo e no preenchimento da linha de cerca quanto em uma linha de conteúdo; as implementações MUST NOT usar uma classe de espaço em branco do runtime aqui, já que elas discordam sobre caracteres como U+0085 e U+00A0 e discordariam sobre a existência de um bloco. Uma marca de ordem de bytes UTF-8 inicial é removida antes do parsing. As linhas são separadas por LF, com um CR final tolerado, linhas em branco e linhas inteiras iniciadas por#são ignoradas (como nos blocos de índice de categoria de content-categories.md), e o arquivo é UTF-8. A varredura é feita por linha e não consulta a estrutura do markdown: uma linha que carregue três ou mais crases e a marca, depois de recuo opcional de espaço ou tabulação, abre um bloco real onde quer que esteja no documento, inclusive dentro de um exemplo cercado mais longo ou dentro de um item de lista. Um exemplo destinado a ilustrar em vez de declarar é, portanto, cercado com uma marca diferente, nunca com um token extra depois deleji-mounts: a marca é aquilo que o scanner reconhece, entãoleji-mounts exampleabre um bloco real e reporta um erro de parsing, enquanto uma cerca marcada comotextnão abre nada.mountMUST coincidir com onamede um mount declarado eownerMUST coincidir com oowner.namedeclarado desse mount, comparados como strings decodificadas; uma entrada para um mount não declarado, uma segunda entrada para um mesmo mount e um mount declarado sem entrada são todos erros. Uma camada que não declara mounts MUST NOT carregar um blocoleji-mounts.A localização da irmã deliberadamente não é um elemento: um mount é materializado em uma projeção local à máquina e endereçada por conteúdo, então um leitor a resolve com
leji mounts locate <name>em vez de inferir um caminho (conforme distribution.md). A prosa em torno do bloco SHOULD explicar o roteamento com naturalidade; o bloco é o núcleo verificável, nunca um substituto dessa prosa nem da declaração emleji.json. As irmãs montadas são fontes distintas e nomeadas, nunca fundidas às categorias da hospedeira; o perfil de boot roteia o agente para dentro de uma irmã apenas quando a tarefa casa com o roteamento dela ou quando o perfil o exige. O que fica sem verificação é deliberado:carrieseread-whensão texto livre, e a fidelidade deles aos metadados de roteamento do mount é atestada pela equipe em vez de verificada pelo ferramental, que checa enumeração, identidade e presença. Expor as irmãs aqui mantém a descoberta de mounts no ponto de entrada em linguagem de tarefa do agente, de modo que seguir o requisito 5 continua não exigindo a leitura do manifesto.
Um exemplo completo de bloco leji-mounts#
Uma entrada, para uma hospedeira que declara um único mount chamado acme-product-context. O bloco fica na coluna 1 do perfil de boot, exatamente como se lê aqui; a cerca externa de quatro crases é o invólucro deste documento e não faz parte dele.
```leji-mounts
- mount: acme-product-context
owner: Product team
carries: product-side domain language and the decisions behind the customer-facing surface
read-when: a task touches product behavior, product terminology, or billing
```
Perfis de agente#
Uma camada de contexto MAY definir perfis específicos por papel (por exemplo um perfil de revisor, um perfil de release, um perfil de QA) sob um diretório declarado por machine.agentProfilesPath. Cada perfil:
-
MUST ser markdown com frontmatter YAML válido segundo
agent-profile.schema.json. -
MUST, uma vez resolvida a herança, carregar o que o papel lê primeiro (
requiredRead) e quando ele precisa parar e perguntar (mustAskWhen). Um perfil que declarainheritsMAY omitir qualquer um dos dois onde sua base o fornece; um perfil que não declara MUST declarar os dois por conta própria. -
MAY declarar
inherits, que é operante na linha 1.0: ele nomeia exatamente um outro perfil do conjunto de perfis da camada, cujoroleMUST sercore, e cuja postura e corpo este perfil estende. O conjunto de perfis da camada é todo documento sob omachine.agentProfilesPathdeclarado, junto com todo documento nomeado no mapaagentsdo manifesto, onde quer que esse documento esteja. A resolução é de um único nível, então um perfil cujoroleécoreMUST NOT declararinherits, e o alvo nomeado MUST existir, MUST ser único poride MUST NOT declararinheritsele próprio. A resolução compõe:- Arrays de postura (
requiredRead,defaultContext,mustAskWhen,mustRefuseWhen): as entradas da base na ordem em que foram escritas, depois as entradas do perfil derivado na ordem delas, descartando qualquer uma que a base já carregue. A ordem em que foram escritas é intenção de carregamento, então nada é ordenado. - Todos os demais campos (
id,name,role,purpose,version,host,invocation,escalation,owners,freshness): os do próprio perfil derivado, nunca herdados.inheritsé uma diretiva de resolução e não faz parte, ele mesmo, do perfil resolvido. - Corpo: os dois corpos são normativos, primeiro o da base, depois o do perfil derivado.
Um consumidor que não consiga resolver um perfil herdado MUST NOT aplicar o arquivo derivado sozinho; o arquivo derivado é metade de um perfil, então o consumidor o reporta como não suportado. Onde uma condição de perguntar e uma condição de recusar se aplicam à mesma situação, a recusa prevalece.
A resolução garante composição, não estreitamento semântico: prosa derivada que contradiga ou enfraqueça a base é não conforme, e nenhum ferramental detecta uma contradição em linguagem natural.
- Arrays de postura (
Os perfis ajustam o que um papel carrega e como ele se comporta; eles não duplicam conteúdo da camada de contexto.
O host e o invocation opcionais de um perfil são o atalho de ator único: eles dizem como engajar o único participante que preenche este papel. O command deles é um modelo que segue a mesma regra dos modelos de comando de ator, incluindo o marcador <prompt> e seu posicionamento (ver context-layer.md, Requisitos). Onde um papel tem mais de um participante elegível, ou onde o mesmo participante precisa de uma invocação diferente conforme o papel que preenche, o registro opcional actors do manifesto carrega isso em vez deles (mesma seção). Um papel usa um mecanismo ou o outro, nunca os dois.
Notas (não normativo)#
O perfil de boot é deliberadamente simples: um mapa e uma postura, não uma base de conhecimento. Se ele ocupar mais do que algumas telas, há no ponto de entrada conteúdo que deveria pertencer a uma categoria.
O problema que esse desenho procura evitar é a indireção: cada salto entre o primeiro contexto de um agente e a restrição real consome atenção. Uma camada de contexto bem implementada não precisa de nenhum ponto de entrada de fornecedor (a invocação pode apontar diretamente para o perfil de boot), e o perfil de boot leva diretamente ao conteúdo. A profundidade deve estar nos documentos da camada de contexto, nunca no caminho até eles.
Todo documento que o perfil de boot manda ler antes de qualquer tarefa é pago em toda tarefa, então o conjunto incondicional é o espaço mais caro da camada de contexto. Mantenha nele só o que é genuinamente universal, e roteie o resto por cargas tipadas por tarefa, pelas categorias, pelo índice e pelo escopo que cada registro de decisão declara. O índice existe para que um agente possa carregar a fatia de que uma tarefa precisa, em vez da árvore inteira; as decisões se acumulam sem limite, então elas são roteadas, nunca pré-carregadas como um diretório.
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.
Decisões
Os registros de decisão documentam, com data, o porquê dentro da camada de contexto: decisões de arquitetura, escolhas de fornecedor, limites de escopo e decisões conscientes de não agir. Eles evitam que tudo seja rediscutido do zero e oferecem aos agentes o raciocínio, não apenas a regra.
Os registros de decisão são o subtipo formal de registro (ver content-categories.md, Intenção e registros): são registros por natureza, com um schema e um ciclo de vida uniformes que os registros genéricos não têm. Suas entradas de índice geradas carregam kind: record; um registro de decisão não declara chave kind própria (o schema é fechado, então um kind explícito falha na validação).
Requisitos#
- Os registros de decisão são markdown com frontmatter YAML válido segundo
decision-record.schema.json, um registro por arquivo. O corpus de decisões é a união de duas superfícies declaradas no manifesto, e uma camada de contexto MAY usar uma delas ou as duas: o caminho de registros declarado (machine.decisionRecordsPath, padrão<root>/decisions/) e as entradas para as quais os arquivos de índice da categoriadecisionsresolvem. Um registro MUST ser alcançável por ao menos uma das duas. - O frontmatter MUST carregar:
id(estável),title,statusedate.statusé um entreproposed,accepted,superseded,deprecatederejected. - O corpo MUST enunciar, em prosa: o contexto (que situação forçou uma decisão), a decisão em si e suas consequências. Os títulos de seção RECOMMENDED são
## Context,## Decisione## Consequences; um registro MAY acrescentar## Alternatives. - Os registros são histórico somente acréscimo: um registro MUST NOT ser editado até virar uma decisão diferente. Dois campos de frontmatter são mutáveis conforme uma decisão envelhece,
status(seu ciclo de vida) esupersededBy(definido quando ela é superada); todo o resto, oid, otitlee adateoriginais, o escopo declarado e o corpo em prosa, é imutável uma vez publicado. Uma reversão ou mudança é um novo registro cujo frontmatter definesupersedes, e ostatusdo registro antigo passa asupersededcomsupersededBydefinido. O elo de superação MUST permanecer consistente nos dois sentidos: quando o registro B definesupersedes: A, o registro A carregastatus: supersededesupersededBy: B, e um registrosupersededMUST nomear seu sucessor emsupersededBy. Os dois registros permanecem. O ferramental de referência hoje impõe a consistência bidirecional da superação. Ele ainda não verifica a imutabilidade em si (que os campos congelados e o corpo de um registro publicado estejam inalterados em relação a uma revisão base); essa é uma verificação reportada no roteiro, ainda não uma verificação bloqueante. Até isso chegar, a imutabilidade se apoia na disciplina de revisão atestada por processo (ver conformance.md). - Um registro MAY declarar
affectedPathseaffectedCategories, para que o ferramental possa rotear do escopo de uma tarefa até as decisões que a governam. Como o escopo de uma tarefa seleciona registros (contenção de caminho ciente de sobreposição, casamento estreito de categoria, o vínculo deaccepted/deprecatede o tratamento de alcance organizacional para um registro que não declara nenhum dos dois) é o algoritmo de Roteamento de tarefas em machine-readable-surface.md. - Propostas rejeitadas também são registros (
status: rejected). Uma decisão não tomada, escrita, é o seguro mais barato que existe contra rediscussão.
Compatibilidade com ADR (não normativo)#
Os registros de decisão do Leji foram projetados para serem compatíveis com Architecture Decision Records: um diretório de ADR existente atende a decisions depois que os campos de frontmatter são acrescentados a cada registro (ou apenas aos novos registros dali em diante) e o diretório é mapeado no manifesto. Nenhuma ferramenta de ADR é exigida ou excluída.
Governança
A governança é o que distingue uma camada de contexto de um wiki. A semântica segue o círculo: o acesso é igual, a autoridade não.
O círculo, normativamente#
- Todo mundo lê. Todos os participantes, pessoas e agentes igualmente, com acesso a uma camada de contexto MUST conseguir ler tudo o que há nela. Uma camada de contexto com leitura restringida por papel dentro dela não é uma camada de contexto compartilhada; onde pessoas diferentes podem ler material diferente, esse material pertence a camadas de contexto separadas (ver Fronteira de acesso, abaixo, e distribution.md).
- Qualquer um propõe. Qualquer participante, pessoa ou agente, MAY propor mudanças na camada de contexto. As propostas escritas por agentes são de primeira classe: um agente que descobre contexto ausente ou errado enquanto trabalha SHOULD propor a correção no mesmo conjunto de mudanças do trabalho que a revelou. Uma proposta SHOULD trazer justificativa suficiente para que quem revisa entenda sua intenção e seu efeito esperado; essa justificativa é o mínimo de que uma pessoa precisa para aprovar. Leji 1.0 não define nenhum protocolo generalizado de evidência (ver Escopo da 1.0).
- Pessoas aprovam. Toda mudança na camada de contexto MUST ser aprovada por uma pessoa antes de se tornar canônica. A aprovação se apoia no mecanismo de revisão já existente do repositório (pull requests); Leji não introduz processo separado. A participação MAY acontecer por qualquer interface, mas a aprovação canônica MUST ser um registro de revisão auditável nesse mecanismo: atribuível à pessoa que aprova e vinculado ao conjunto de mudanças em revisão. Uma aprovação expressa apenas em discussão externa, chat, estado de ticket ou comentários de documento não conta até virar um registro assim; espelhá-la em um comentário não basta. Ampliar como as pessoas participam nunca move onde a autoridade é registrada.
Requisitos#
- Propriedade, não autoria. O manifesto MUST nomear um dono primário (
owners.primary) e MAY nomear um dono de continuidade (owners.continuity): uma pessoa diferente que assume a mesma responsabilidade quando o primário está indisponível ou se afasta. Uma adoção assistida SHOULD nomear o dono de continuidade antes que a ajuda externa vá embora; uma camada de contexto individual MAY não ter nenhum, o que sinaliza honestamente que ela não tem sucessão. Os donos são pessoas responsáveis: um agente propõe e revisa, mas nunca é dono, e nomear o primário de novo como continuidade não fornece nenhuma. O dono responde pela saúde da camada de contexto: que ela se mantenha atual, que conteúdo desatualizado ou contraditório seja podado e que toda área tenha alguém que cuide dela. Os donos não são guardiões do conteúdo. O conteúdo é escrito e mantido verdadeiro pelo círculo inteiro à medida que ele trabalha; concentrar isso em um único guardião é o gargalo que este modelo existe para evitar. - Escopo da revisão. As mudanças na camada de contexto SHOULD ser revisadas pelas pessoas mais próximas do conteúdo afetado, os donos de área, e não canalizadas por um ponto único de aprovação. A propriedade de área é o mapa de propriedade já existente do repositório (um arquivo
CODEOWNERS, uma convenção da equipe), não um campo novo no manifesto; Leji o reaproveita do mesmo jeito que reaproveita os pull requests para a aprovação. O dono primário responde por garantir que toda área tenha um. A revisão pergunta mais que “isto é verdade?”: por que isto pertence à camada de contexto, quem vai depender disso, o que mostra que se sustenta e quando deve ser reexaminado. Uma mudança que não consegue responder a isso é um link ou uma nota, não contexto canônico. A pergunta permanente de revisão para qualquer conjunto de mudanças é esta mudança alterou o contexto?; se sim, o delta de contexto pertence ao mesmo conjunto de mudanças. - Inclusão e remoção. Propor é aberto; incluir não é. Um conteúdo pertence à camada de contexto apenas se muda como o trabalho futuro é feito: se estabelece uma restrição, codifica uma decisão, define uma interface ou uma fronteira de propriedade, ou impede um erro repetido. Todo o resto é apontado por link, não absorvido. A camada de contexto MUST ter um caminho de remoção tão deliberado quanto seu caminho de aprovação: conteúdo desatualizado, superado e duplicado é podado em conjuntos de mudanças revisados comuns, e podar faz parte do dever de cada dono de área, não é um projeto de limpeza à parte. Uma camada de contexto que só cresce é uma que apodrece mesmo passando pela revisão. Orientação duradoura SHOULD atender ao critério de comprovação em duas tarefas (conforme content-categories.md): uma correção pontual pode ser incorporada, mas uma norma só se torna canônica depois de ter se sustentado em pelo menos duas tarefas reais. Capture o que é verdade, não o que se espera que seja.
- Disciplina de changelog. Na conformidade
indexede acima, toda mudança aprovada na camada de contexto MUST acrescentar uma entrada de changelog legível por máquina conforme machine-readable-surface.md. - Atualidade. A atualidade é um mecanismo de intenção: documentos de intenção e perfis de agente SHOULD carregar horizontes de revisão (
freshness.reviewAfternas entradas de índice e nos perfis), e um registro não carrega nenhum (sua data é sua atualidade, conforme content-categories.md; um horizonte declarado em um registro é um erro de validação). O ferramental SHOULD reportar conteúdo de intenção cujo horizonte já passou, e MUST NOT tratar silenciosamente conteúdo desatualizado como atual. Um leitor que carrega contexto para uma tarefa MUST expor, na saída dessa tarefa, qualquer item carregado cujo horizonte de revisão já passou, para que a desatualização fique visível para a pessoa em vez de enterrada. Se o próximo registro esperado de uma série operacional está atrasado é uma noção distinta (recência de fluxo); a 1.0 a nomeia e não define mecanismo para ela. O contexto obrigatório de uma tarefa é a união do conjunto de carregamento incondicional do perfil de boot, dorequiredReaddo perfil de agente ativo e da fatia que o algoritmo de Roteamento de tarefas seleciona para a tarefa (suas decisões vivas roteadas e seus documentos de intenção governados roteados, mais qualquer registro que os caminhos da tarefa selecionem diretamente, conforme machine-readable-surface.md). Quando o horizonte de um item obrigatório expirou, o leitor MUST parar ou perguntar em vez de seguir em frente com ele; agir sobre contexto sabidamente vencido em sua revisão é a falha de desatualização silenciosa que esta regra existe para impedir. Um item desatualizado que não seja obrigatório MAY ser usado com a desatualização registrada. Na conformidadegoverned, os horizontes de atualidade MUST ser declarados e verificados (conforme o checklist de conformidade, uma verificação apenas de relato é aceitável); rodar a verificação no CI é RECOMMENDED. A atualidade de revisão (acima) é distinta da atualidade do checkout: se a cópia que um leitor tem corresponde ao repositório canônico. Um leitor estabelece a atualidade do checkout a partir do sistema de controle de versão (git); a árvore de trabalho só é atual até a revisão em checkout, e o ferramental MUST NOT tratar silenciosamente uma cópia não verificada como atual. Um leitor que alcança a camada de contexto como conteúdo de arquivo simples, sem árvore de trabalho git acessível nem metadados de versão (conteúdo de arquivo enviado ou sincronizado para outra interface, sem o repositório), MUST tratar a atualidade do checkout como desconhecida em vez de atual. - O conteúdo canônico vive na camada de contexto. Conhecimento que governa como o trabalho é feito MUST NOT existir apenas em um arquivo de configuração de fornecedor, em uma conversa de chat ou nas anotações de uma pessoa. Se governa o trabalho, pertence à camada de contexto, sob revisão.
Fronteira de acesso#
Leji não define mecanismo próprio de controle de acesso. O acesso a uma camada de contexto é governado pelo sistema de controle de versão (git) e pela plataforma em que o repositório vive: as permissões do host do repositório e o sistema de arquivos ou disco compartilhado que expõe a árvore de trabalho. A camada de contexto é a unidade de acesso.
- Uma camada de contexto MAY viver em um repositório com controle de acesso. Leji não concede, verifica nem impõe esse acesso; quem faz isso é o sistema de controle de versão e seu host.
- Uma camada de contexto conforme MUST NOT exigir leitura restringida por papel dentro dela mesma. “Todo mundo lê” tem como escopo o público de uma camada de contexto: todo mundo que o sistema de controle de versão admite lê toda aquela camada de contexto.
- Conteúdo que precisa de um público mais estreito (um contexto executivo, financeiro, de segurança ou de resposta a incidentes) MUST viver em uma camada de contexto separada, com repositório, manifesto, dono e processo de revisão e aprovação próprios, com permissões dadas pelo sistema de controle de versão. Contexto restrito é uma camada de contexto separada, nunca uma região restrita de uma camada compartilhada.
- Compor uma camada restrita dentro do contexto de outra equipe é o caso de federação, com as regras adicionais para mounts restritos em distribution.md.
O modelo de manutenção (não normativo)#
A camada de contexto é mantida uma mudança por vez, como parte do trabalho que já está em andamento: uma tarefa revela um contexto ausente ou incorreto; a correção segue no mesmo conjunto de mudanças revisado; o changelog a registra. Não existe uma sprint separada de documentação, nem precisa existir. Um contexto incorreto produz uma saída incorreta cujo impacto alguém percebe no mesmo dia; somados à revisão e às verificações mecânicas no CI, esses elementos formam todo o sistema de imposição.
Esse retorno rápido pega conteúdo errado depressa. O acúmulo lento, conteúdo que é apenas medíocre ou redundante, é pego pela régua de inclusão e pelo caminho de remoção acima, aplicados pelas pessoas que são donas de cada área. Isso é curadoria, e Leji a distribui de propósito. Um guardião único parece o jeito seguro de sustentar a qualidade e é o contrário: ele vira o caminho mais lento do sistema, as mudanças se enfileiram atrás dele ou o contornam, e ele sustenta menos contexto que os especialistas de cada área. A camada de contexto ou trava ou se bifurca. É cada dono de área podando e filtrando a própria fatia que mantém a camada de contexto inteira pequena e verdadeira sem um ponto de estrangulamento. O dono cuida da saúde do sistema; o círculo cuida do conteúdo.
Distribuição
Esta seção define onde a camada de contexto fica em relação ao trabalho que descreve. Há três padrões e uma regra comum a todos: a camada de contexto é somente documentação e MUST NOT introduzir uma dependência de build ou de runtime em nenhum repositório consumidor.
Padrão 1: Monorepo (padrão)#
A camada de contexto vive no mesmo repositório que o código e a infraestrutura que descreve, na raiz do contexto. Este é o padrão RECOMMENDED sempre que o trabalho da equipe vive em um único repositório: código, infraestrutura e contexto são versionados juntos, e o desvio fica estruturalmente difícil.
Padrão 2: o submódulo somente documentação para arranjos multirrepo#
Quando o trabalho abrange muitos repositórios, a camada de contexto vive em um repositório de contexto dedicado, e os repositórios consumidores a montam como um submódulo git.
- O repositório de contexto é um repositório git normal, com seu próprio
leji.json, suas políticas de branch e seu processo de revisão e aprovação. - Os repositórios consumidores MUST montá-lo em um caminho fixo (RECOMMENDED:
context/) e MUST NOT acoplar nenhuma etapa de build ou de runtime à presença dele: um mount ausente ou desatualizado degrada o conhecimento, nunca o build. - Cada repositório consumidor fixa uma versão específica da camada de contexto. As atualizações de pin MUST chegar como conjuntos de mudanças revisáveis (pull requests scriptados ou abertos por bot), para que as mudanças de contexto sejam visíveis, revisáveis e atribuíveis por repositório.
- O ferramental SHOULD reportar pins desatualizados (o quanto cada repositório consumidor está atrás da camada de contexto). O relato de pin desatualizado MUST preceder qualquer imposição bloqueante: primeiro a visibilidade, depois as verificações bloqueantes. O relato de pin do SDK de referência da 1.0 cobre os mounts de federação (padrão 3); para os pins do lado consumidor deste padrão ele não traz verificação, então, em
federated, este item é atestado por processo (ver conformance.md); uma equipe ou o ferramental dela o reporta até que uma verificação de referência chegue.
Padrão 3: federação de camadas de contexto irmãs#
Os padrões 1 e 2 têm, cada um, uma única camada de contexto: um monorepo é dono de uma, e uma organização multirrepo consome uma. A federação é o padrão para uma organização em que mais de uma equipe já é dona de uma camada de contexto própria, e o objetivo é tornar essas camadas de contexto legíveis umas para as outras sem que ninguém abra mão do controle.
O primeiro impulso é fundi-las em um repositório de contexto que concentre o conhecimento de todas as equipes. Resista a ele. Uma camada de contexto se mantém atual porque seus donos a leem em todas as tarefas e corrigem os erros no mesmo conjunto de mudanças. Ao transferir o contexto de produto para o repositório de plataforma, você separa o conteúdo de produto de quem responde por ele; o conteúdo se deteriora enquanto todos supõem que outra pessoa assumiu a responsabilidade. Centralizar o conhecimento recria o gargalo que, antes de tudo, o confinou à memória e às conversas.
A federação compõe as camadas de contexto em vez de absorvê-las. A camada de contexto de uma equipe entra no grafo de outra equipe como uma irmã: montada, referenciada e lida, nunca copiada.
-
Uma camada de contexto irmã entra como um mount fixado declarado no
federation.mountsdo manifesto da hospedeira: onamee oownerda irmã, seu localizador de repositóriosourcee umpinnomeando o id de commit imutável e completo da revisão da irmã que a hospedeira lê. O pin é a versão de referência do mount, guardada no próprio manifesto para que as atualizações de pin cheguem como conjuntos de mudanças revisáveis: um mount registra qual versão da verdade de outra equipe este repositório estava lendo, não um fork dela. Um mount MAY declarartrackingRef, um branch ou tag totalmente qualificado na origem contra o qual a desatualização e a alcançabilidade são julgadas; na ausência dele, o branch padrão anunciado pela origem é usado no momento da verificação e nomeado no relatório. -
A irmã mantém tudo o que a faz viva: seu próprio repositório, dono, processo de revisão e aprovação, changelog e declaração de conformidade. A hospedeira MUST NOT copiar o conteúdo da irmã para dentro de si. Conteúdo separado da equipe que é dona dele fica desatualizado sem ninguém responsável, que é exatamente a falha que a federação existe para impedir.
-
O conteúdo montado se materializa como uma projeção de camada hidratada pelo resolvedor, nunca como uma cópia commitada. Uma irmã costuma ser uma camada embutida em um repositório maior (padrão 1), então fazer checkout de todo o repositório da irmã significaria incorporar um produto como dependência só para ler o contexto dele. Em vez disso, o ferramental extrai a projeção de camada no pin, a união sem duplicatas de tudo o que o próprio manifesto da irmã torna legível (o
leji.jsonda raiz, a árvore sob a raiz do contexto declarada, o perfil de boot, os arquivos de índice e de changelog de máquina quando presentes no pin, as árvores de perfis de agente e de registros de decisão quando presentes, todo perfil de agente nomeado pelas ligações deagents, todo arquivo de índice de categoria e todo caminho governado que o índice de contexto gerado e fixado lista, onde quer que essas coisas estejam; o próprio manifesto da irmã define a projeção dela, a hospedeira nunca faz a curadoria dela), para um cache efêmero que o controle de versão da hospedeira ignora. A fronteira de falha segue a mesma linha: um arquivo referenciado ou exigido pelo schema ausente no pin (o perfil de boot, um índice de categoria, um perfil de agente ligado, um caminho governado indexado) faz a projeção falhar com um código estável que nomeia o artefato declarante e o caminho ausente, enquanto um diretório ausente ou um artefato de máquina ausente não contribui com nada e não faz nada falhar, tenha sua localização efetiva sido declarada ou assumida por padrão; o git não consegue representar um diretório vazio, e uma camada sem índice gerado simplesmente não tem fecho de conteúdo além da árvore da sua raiz. Uma falha de projeção da classe de disponibilidade (o conteúdo do pin está ausente ou malformado) deixa o mount indisponível nesta máquina e nunca faz falhar a validação comum da hospedeira nem o build do produto dela. Uma falha de projeção de segurança ou interna (um caminho que escapa, uma string malformada, um limite excedido) aborta a hidratação com saída não zero, e nenhuma projeção parcial é jamais publicada. Os bytes fixados são resolvidos a partir de um armazenamento de objetos git (um repositório de dica local à máquina, o armazenamento gerenciado pelo resolvedor ou a base de objetos de um submódulo da hospedeira), nunca a partir de árvore de trabalho alguma, e o acesso à rede acontece apenas como uma etapa explícita e consentida. Um repositório hospedeiro MAY manter um submódulo da irmã por razões próprias; o ferramental o trata apenas como mais um armazenamento local de objetos, e um checkout nunca é conteúdo montado legível. Commitar conteúdo da irmã dentro da hospedeira, inclusive o conteúdo do cache, não é conforme. A regra de somente documentação do padrão 2 se sustenta por construção: nada na hospedeira compila ou executa contra a projeção.Um caminho de máquina que resolve para a raiz do repositório não seleciona nada. Um
machine.agentProfilesPathoumachine.decisionRecordsPathdeclarado que resolva para a raiz do repositório da irmã não contribui com seleção de diretório alguma para a projeção: honrá-lo incorporaria o repositório inteiro da irmã como dependência, que é justamente o resultado que a projeção de camada existe para evitar. Nada referenciado se perde, porque perfis e registros de decisão nomeados individualmente, pelas ligações deagentsou pelo índice gerado e fixado, continuam viajando; só a seleção geral da raiz é descartada.Limites da projeção. Um resolvedor MUST impor quatro limites, para que uma implementação independente recuse as mesmas entradas em vez de cada uma escolher o próprio teto. Uma projeção carrega no máximo 65.536 entradas, contadas depois da remoção de duplicatas. Seu conteúdo totaliza no máximo 2 GiB (2.147.483.648 bytes). Nenhum caminho projetado isolado passa de 4.096 bytes, medidos como a codificação UTF-8 do caminho, e não como seus caracteres, pontos de código ou quaisquer unidades nativas de string de um runtime, que diferem entre implementações e, de outro modo, aceitariam caminhos diferentes. Uma listagem de árvore inteira ocupa no máximo 256 MiB (268.435.456 bytes) em transporte; isso limita os metadados de enumeração que um resolvedor lê para poder selecionar, e não o conteúdo projetado que o limite de bytes restringe, e os dois são números deliberadamente diferentes porque um repositório grande pode guardar uma projeção válida perfeitamente pequena. Exceder qualquer um dos quatro é uma falha de projeção da classe de segurança.
-
Um mount que não está materializado degrada o conhecimento, nunca o build. A validação separa três preocupações. Um manifesto que mente é um erro: nomes de mount duplicados, um mount reutilizando o próprio
nameda hospedeira, ou umsourceoupinausente ou malformado. Um mount declarado que simplesmente não está hidratado nesta máquina é um aviso: disponibilidade degradada de forma honesta, reportada e pulada. A integridade de uma projeção materializada em relação ao seu pin é um diagnóstico exposto pelo ferramental, fatal apenas sob imposição explicitamente ativada. A validação comum MUST NOT falhar, buscar na rede nem perguntar por causa de um mount indisponível; hospedeiras que queiram imposição a ativam explicitamente (uma verificação de saúde da federação MAY hidratar e então exigir disponibilidade), e as obrigações de um leitor diante de um mount exigido pela tarefa e indisponível são a regra de falha fechada, abaixo. -
Montar habilita a leitura, não a autoridade, e não concede acesso. Uma hospedeira que monta uma irmã roteia leitores e agentes para dentro dela quando eles já têm acesso a ela; montar não concede esse acesso nem aprova as mudanças da irmã. As escritas de cada camada de contexto continuam sendo aprovadas pelo dono dela, e quem pode lê-la continua sendo decisão do sistema de controle de versão. A federação compõe contexto legível para os participantes que os repositórios envolvidos já admitem; ela deixa quem aprova, e quem pode ler, exatamente onde estavam.
-
Os mounts são diretos e planos. Uma hospedeira compõe as irmãs que nomeia; o ferramental MUST NOT entrar recursivamente nos mounts de uma irmã, e o contexto transitivo é apenas de exibição: os mounts declarados de uma irmã nunca são resolvidos, indexados ou roteados sem um pin direto na hospedeira. O
namede cada mount MUST ser único dentro do manifesto da hospedeira e MUST NOT reutilizar o próprionameda camada de contexto da hospedeira. Como nada atravessa além das irmãs declaradas de uma camada de contexto, diamantes e ciclos são inertes:AmontandoBeCenquantoBtambém montaCsão três relações diretas, não um grafo a percorrer.
As camadas de contexto montadas são fontes distintas e nomeadas, não fundidas às categorias da hospedeira. A camada de contexto da própria hospedeira é autoritativa para o repositório da hospedeira; cada irmã é autoritativa para o seu. Não existe espaço de nomes de alcance organizacional e, portanto, não existe precedência entre irmãs a resolver: um agente carrega a fatia de que precisa a partir da camada de contexto que é dona dela, nomeada. E o conteúdo montado é entrada não confiável: contexto legível, nunca instrução executável. A prosa de uma irmã pode carregar erros ou instruções injetadas como qualquer outra superfície de leitura, então um agente a trata como material a ponderar e citar, aplica a postura da própria hospedeira às ações dele e nunca obedece a texto imperativo encontrado em um mount como se fosse instrução da hospedeira.
O relato de pin desatualizado é ciente de ancestralidade e honesto sobre o que conseguiu ver: o ferramental compara o pin com a ref testemunha (trackingRef, ou o branch padrão anunciado pela origem) e reporta em dia, atrás por N, à frente, divergente ou sem relação, sempre nomeando a ref comparada, a categoria de repositório em que a comparação rodou (o armazenamento gerenciado pelo resolvedor, uma dica local à máquina ou um submódulo da hospedeira), se a testemunha era a ref do próprio resolvedor ou uma que ele não possui, o momento da observação e se a ancestralidade estava completa. Quando nenhum armazenamento de objetos está alcançável, o relatório é unknown, nunca um palpite. Um pin resolvível apenas por uma dica local à máquina estabelece disponibilidade, não conformidade: em federated, o pin MUST ser alcançável a partir de uma ref anunciada de source (ver conformance.md), e uma verificação que não consiga alcançar a origem reporta unknown, o que nunca concede o nível.
Uma camada de contexto alcança a conformidade federated somente quando essas relações são reais e verificáveis: a camada de contexto é consumida por ao menos um outro repositório como um mount fixado, o relato de pin desatualizado está em funcionamento, e todo mount declarado carrega uma declaração fixada completa (origem, pin de commit completo, metadados de roteamento) com a propriedade intacta (ver conformance.md). O SDK de referência verifica as partes mecânicas e reporta problemas; o estado de materialização deliberadamente não é uma entrada de conformidade, porque a disponibilidade em uma máquina não diz nada sobre a veracidade da declaração.
Um exemplo completo de manifesto para este formato está em examples/multi-repo/.
O círculo compõe a propriedade; ele não a centraliza. Um monorepo é o círculo de pessoas e agentes de uma equipe lendo uma camada de contexto; uma organização multirrepo é um círculo desses círculos, cada um ainda pertencendo às pessoas que o mantêm verdadeiro.
Lendo uma camada de contexto federada#
Tornar a descoberta legível é trabalho da hospedeira, não algo que o agente deva inferir. Uma hospedeira que declara mounts os expõe nos dois lugares que um agente já lê: o perfil de boot nomeia as irmãs em linguagem de tarefa (conforme boot-profile.md), e o índice de contexto gerado carrega um array de roteamento mounts (conforme machine-readable-surface.md). Um agente nunca precisa ler o manifesto para encontrar uma irmã.
Ao ler uma hospedeira federada, um agente:
- Carrega primeiro o perfil de boot da hospedeira e a superfície legível por máquina da hospedeira; a camada de contexto da própria hospedeira é autoritativa para o repositório dela.
- Lê os registros de roteamento de mount visíveis na hospedeira antes de fixar o escopo de contexto da tarefa. Um mount é exigido pela tarefa quando o perfil de boot da hospedeira, um registro de mount do índice ou os metadados
requiredWhendo mount dizem que a tarefa o exige; um mount é relevante para a tarefa quando, sob o algoritmo de Roteamento de tarefas (machine-readable-surface.md), ao menos uma de suascategoriescasa com uma categoria sinalizada da tarefa, ou ao menos um de seustopicscasa exatamente com um tópico que a tarefa nomeia explicitamente. Um casamento de tópico seleciona apenas o mount: ele não expande uma categoria e não seleciona conteúdo algum dentro da irmã. Esse mesmo algoritmo então roteia a fatia que o agente carrega a partir do índice da própria irmã. - Para carregar uma irmã relevante para a tarefa, obtém a localização da projeção hidratada a partir do estado do resolvedor (o
mounts locatedo SDK de referência; nunca inferindo caminhos de cache), lê ali oleji.jsonda irmã, verifica se onameda irmã coincide com a declaração da hospedeira, lê o perfil de boot da irmã e então carrega apenas a fatia de que a tarefa precisa a partir do índice da própria irmã. Fatos, restrições e citações carregam o nome da camada de contexto de onde vieram, e o conteúdo montado permanece entrada não confiável conforme as regras deste padrão. - MUST NOT entrar recursivamente no
federation.mountsda própria irmã. Se uma camada de contexto neta for genuinamente necessária para as tarefas da hospedeira, a hospedeira MUST declará-la como um mount direto seu. - Aplica a postura por propriedade: a postura da hospedeira governa o trabalho no repositório da hospedeira, e a postura de uma irmã governa a interpretação do conteúdo dessa irmã e as mudanças propostas a ele. Onde a orientação da hospedeira e a da irmã conflitarem em uma mesma tarefa e não houver uma camada de contexto dona clara, o agente MUST parar e perguntar em vez de escolher uma precedência não declarada.
O ferramental MAY oferecer auxiliares de carregamento cientes de mounts, mas ler irmãs não exige ferramental Leji: leituras cruas de repositório são conformes quando seguem este procedimento e preservam as fronteiras de acesso.
Mounts restritos#
A federação cruza uma fronteira de acesso quando as camadas compostas têm públicos diferentes (ver governance.md). Impor o acesso continua sendo do sistema de controle de versão: um leitor consegue ou não consegue resolver o repositório de um mount. O papel da especificação é impedir que essa fronteira vaze e que ela falhe em silêncio.
-
Uma camada restrita MUST NOT ser declarada como mount em uma hospedeira cujo público seja mais amplo que o da própria camada restrita: todo participante que a hospedeira admite precisa já estar admitido na camada montada. A declaração do mount em si (sua presença,
name,owner,role,categories,topics,requiredWhen,source,pinetrackingRef) MUST NOT revelar nada que o público da hospedeira não possa ver. Onde um público mais amplo precisa de uma decisão restrita, publique uma camada complementar expurgada do conteúdo restrito ou um resumo público da decisão, não um mount para a camada restrita. -
Um mount é uma referência, não uma concessão de acesso. Declarar um mount nunca amplia quem pode ler a camada montada além do que o sistema de controle de versão já permite; se um dado leitor consegue resolvê-la é decidido lá, não pelo manifesto da hospedeira.
-
Falhe fechado, nunca em silêncio. Um leitor que não consiga resolver um mount exigido pela tarefa (como definido em Lendo uma camada de contexto federada) MUST parar e reportar contexto incompleto. Ele MUST NOT seguir em frente como se a camada inacessível não existisse: um agente agindo sobre contexto parcial que não consegue enxergar é a falha que esta regra existe para impedir. O inverso também é uma falha: um leitor que consegue resolver uma irmã exigida ou relevante para a tarefa mas a pula assim mesmo está agindo sobre contexto silenciosamente incompleto e não é conforme.
O que o ferramental pode e não pode sustentar aqui: um validador reporta a disponibilidade local (um mount declarado sem projeção hidratada aqui), e o algoritmo de roteamento decide a relevância para a tarefa por sobreposição de categoria e de tópico (os SDKs de referência expõem os mounts relevantes para a tarefa; ver machine-readable-surface.md, Roteamento de tarefas). Mas a exigência pela tarefa depende de
requiredWhen, que são condições de tarefa em texto livre, e a alcançabilidade em tempo de execução depende do acesso do próprio leitor no momento da leitura; os dois cabem ao agente julgar, não a uma ferramenta. O MUST de falha fechada é, portanto, atestado pelo agente: o ferramental expõe o que consegue ver, e o agente impõe a parada.
Notas (não normativo)#
A má fama do padrão de submódulo vem de submódulos de código com acoplamento de build. Uma folha somente documentação não tem nenhum desses modos de falha: nada compila contra ela, nada quebra quando ela fica para trás, e o pin é apenas um registro de “de qual versão da verdade este repositório estava trabalhando”, o que é informação, não risco.
A federação parece ter mais partes móveis que uma fusão, e tem menos. Uma fusão é barata uma vez e cara para sempre: toda edição entre equipes daí em diante passa por quem é dono do repositório central, e as partes que nenhuma equipe lê diariamente são as partes que apodrecem. Os mounts irmãos mantêm cada camada de contexto pequena, com dono e lida, e pagam apenas o preço de uma atualização de pin, que é um diff revisável, não uma reunião.
Conformidade
A adoção parcial é intencional. Há quatro níveis, cada um incluindo o anterior, e a equipe declara o seu no manifesto (conformance.claimedLevel). A declaração é sempre própria: não existe programa de certificação.
A conformidade é avaliada com base na camada de contexto materializada no local em que a verificação é executada, não em uma camada canônica que uma cópia talvez represente. Uma cópia acessada sem o repositório correspondente é lida no modo degradado de context-layer.md, e a leitura degradada nunca leva à autoridade canônica: essa cópia não pode ser verificada, e as ferramentas deixam isso explícito em vez de manter a questão em aberto.
A maior parte dos itens do checklist é verificada por máquina: o ferramental de referência os checa contra a camada e reprova uma declaração que eles não sustentem. Quatro resultados são reportados, e eles deliberadamente não são intercambiáveis:
fail: a evidência foi reunida e o requisito não é atendido.- (atestado por processo), reportado como
manual: o item descreve uma prática da equipe (um processo de revisão e aprovação, um job de CI, um consumidor externo) que nenhuma ferramenta consegue confirmar apenas a partir do repositório, então a equipe responde por ele. Só os itens marcados com (atestado por processo) abaixo são reportados dessa forma. unknown: um item de máquina cuja evidência foi impossível de obter nesta execução, como a verificação federada de alcançabilidade do pin sem acesso à origem, ou a disciplina de somente acréscimo sem uma linha de base git para comparar.unknownnunca concede um nível, e nunca refuta uma declaração que uma execução com evidência poderia confirmar.not applicable: um item de máquina condicional que não se aplica a esta camada, como os itens federados de mount em uma camada que não declara mounts. Ele não é pontuado, e não é evidência em nenhuma direção.
O verifiedLevel que o ferramental reporta é o nível mais alto cujos itens aplicáveis verificados por máquina passam todos, nunca acima do nível que a camada declara; fail e unknown ambos impedem a concessão, e itens atestados por processo ou não aplicáveis não são pontuados. O teto sobre a declaração é deliberado: a verificação responde se a declaração se sustenta, não o que a camada poderia declarar, então uma camada que declara core cuja evidência a levaria a governed ainda reporta core, e o jeito de elevar o nível reportado é elevar a declaração. O verifiedLevel nunca afirma os itens atestados por processo, então um verifiedLevel aprovado é necessário, mas não suficiente, para um nível que os carregue. Cada item abaixo é verificado por máquina, a menos que esteja marcado com (atestado por processo).
Dois itens verificados por máquina se comportam de forma diferente em uma cópia degradada, e a diferença decorre da evidência que cada um tem. A presença do git é respondida: uma cópia que não está em um repositório git não atende ao requisito de core de que a camada de contexto viva em um, então o item é fail. A disciplina de somente acréscimo do changelog não é respondida: o arquivo pode estar inteiramente bem formado enquanto o estado commitado anterior necessário para a comparação está inalcançável, então o item é unknown e a camada simplesmente não se verifica em indexed a partir daquela cópia. Nenhum dos dois é reportado como manual, que fica reservado aos itens marcados como atestados por processo. À parte disso, a regra de atualidade para o leitor (expor contexto carregado desatualizado, e parar ou perguntar diante de um item obrigatório vencido, conforme governance.md) descreve o comportamento do leitor, não uma verificação que condiciona a conformidade: o leji route de referência carimba cada documento roteado com seu horizonte de revisão e seu vencimento, para que um agente possa aplicá-la.
Três itens são hoje verificados com menos profundidade do que sua intenção declarada, e a lacuna é nomeada aqui em vez de ser deixada para o leitor descobrir. O item do perfil de boot é verificado como presença no caminho declarado e como presença dos cabeçalhos de identidade, carregamento e postura (toda execução de validate reporta um cabeçalho ausente como um aviso boot-profile-sections, sem bloquear a validação), e se a seção de identidade diz algo substantivo depende do lint opcional --content, que também sinaliza texto de espaço reservado em qualquer ponto do perfil. O item da decisão real é verificado como frontmatter válido segundo o schema em ao menos um registro resolvido; a substância do corpo (uma decisão de verdade, não um esboço) também depende de --content. O item do changelog é o terceiro: a disciplina de somente acréscimo é verificada contra o estado do arquivo em HEAD, o que pega uma reescrita ainda na árvore de trabalho, que é o caso para o qual existe um hook de pre-commit. Em um checkout de integração contínua, a árvore de trabalho é o HEAD, então uma reescrita que chega já commitada não é visível para a verificação, e quem a cobre é a revisão do conjunto de mudanças. O item, portanto, verifica a árvore de trabalho, não o histórico. A intenção declarada em cada um dos três itens permanece normativa quanto ao que uma camada de contexto conforme carrega; aprofundar as verificações de máquina, e comparar o changelog contra uma revisão base explícita, estão no roteiro do ferramental de referência. A verificação de federated exige adicionalmente ao menos uma entrada declarada em federation.mounts: uma camada de contexto apenas provedora (consumida por outros repositórios, mas que não declara mounts próprios) se verifica em governed, e sua posição federada se apoia nos itens de consumo atestados por processo.
Nível 1: core#
Existe uma camada de contexto e tanto pessoas quanto agentes conseguem trabalhar a partir dela.
- A camada de contexto vive em um repositório git, versionada junto com o trabalho que descreve (conforme context-layer.md, Requisitos).
-
leji.jsonna raiz do repositório, válido segundo o schema do manifesto. - Um perfil de boot no caminho declarado, cobrindo identidade, carregamento e postura.
- Ao menos
domainousystemmapeada (por seus arquivos de índice) e populada com ao menos um documento de intenção resolvido (só registros não carregam contexto operacional), maisdecisionscom ao menos um registro de decisão real: um registro que carregue umstatusconcreto e uma decisão de verdade no corpo, não um esboço vazio ou um texto de espaço reservado. - Um dono primário nomeado.
- Os arquivos de entrada de fornecedor, se presentes, redirecionam para o perfil de boot.
Nível 2: indexed#
A camada de contexto é legível para o ferramental.
- Tudo de
core. - Um índice de contexto gerado, atualizado com a árvore.
- Um changelog legível por máquina; as mudanças na camada de contexto acrescentam entradas.
Nível 3: governed#
Os mecanismos de imposição são mecânicos, não de boa vontade.
- Tudo de
indexed. - As mudanças na camada de contexto passam pelo processo de revisão e aprovação do repositório; pessoas aprovam. (atestado por processo)
- Perfis de agente (ao menos um com
role: core) válidos segundo o schema de perfil. - O CI valida a superfície: manifesto, índice condizente com a árvore, disciplina de changelog, frontmatter de perfil, caminhos declarados que resolvem. (atestado por processo)
- Os horizontes de atualidade são declarados e verificados (uma verificação que apenas relata o resultado é aceitável).
Nível 4: federated#
A camada de contexto abrange uma organização multirrepo.
- Tudo de
governed. - A camada de contexto é consumida por ao menos um outro repositório como um mount fixado, com as atualizações de pin chegando como conjuntos de mudanças revisáveis. (atestado por processo)
- O relato de pin desatualizado está em funcionamento: os consumidores conseguem ver o quanto seus pins ficam atrás da ref testemunha. O relatório ciente de ancestralidade do SDK de referência cobre os mounts de federação declarados; o relato do lado do consumo além disso é da equipe. (atestado por processo)
- Quaisquer camadas de contexto irmãs são declaradas como mounts fixados completos conforme distribution.md: um
sourcenormalizado e umpinde commit completo, com a propriedade intacta. O estado de materialização em qualquer máquina não é entrada de conformidade. - O pin de cada mount declarado é alcançável a partir de uma ref anunciada de seu
source(otrackingRefdeclarado, ou o branch padrão da origem). Esta verificação precisa de acesso à origem: sem ele o resultado éunknown, eunknownnunca concede o nível. Um pin resolvível apenas por uma dica local à máquina é disponibilidade, não conformidade. - Cada mount declarado carrega metadados de roteamento: ao menos
categories, maistopicsourequiredWhen, para que um agente possa decidir a relevância sem ler a irmã. - O perfil de boot expõe toda irmã montada, e o índice gerado carrega o array de roteamento
mounts, para que um agente descubra e carregue as irmãs sem ler o manifesto (conforme boot-profile.md, machine-readable-surface.md).
Notas (não normativo)#
core é o mínimo que torna uma camada de contexto real, indexed acrescenta a superfície gerada que o ferramental lê, governed é onde a camada de contexto deixa de depender da disciplina de alguém, e federated é para organizações em que mais de uma equipe já é dona de uma camada de contexto que vale manter inteira. A maioria das equipes deveria chegar a governed e parar por aí; federated existe para aquelas organizações, não como um selo de maturidade.
Versionamento
Três elementos têm versionamento independente: a especificação, os schemas e quaisquer ferramentas que a implementem.
A especificação#
- A especificação carrega uma versão SemVer (atualmente 1.0.0). Mudanças incompatíveis exigem uma versão maior; toda mudança é registrada no changelog do repositório.
- Uma camada de contexto declara em
leji.jsona linha de especificação que tem como alvo, por meio da chave autonomeadoraleji(por exemplo"leji": "1.0"), seguindo a convenção do OpenAPI. O valor é a linha da especificação (major.minor), nunca a versão de correção dela: um lançamento de correção (1.0.0para1.0.1) refina redação ou ferramental sem mover a linha, então o manifesto continua"1.0"em toda correção. O ferramental MUST validar uma camada de contexto contra a linha declarada, não contra a mais nova.
Linhas de prévia#
Uma linha de especificação MAY ser designada como prévia. Uma linha de prévia é revisável no lugar: ela MAY mudar de maneiras que de outro modo seriam incompatíveis, em vez de ser promovida a uma nova versão, até ser congelada na disponibilidade geral (GA). A regra “mudanças incompatíveis exigem uma versão maior” (item 1) e a regra “o $id se move em uma mudança incompatível de formato” (item 3) valem a partir do congelamento no GA, não enquanto uma linha está em prévia. No GA, a linha é congelada e as duas regras passam a valer.
Uma linha publicada antes da disponibilidade geral MUST declarar isso em seu lançamento inicial.
A linha 1.0 está congelada na versão publicada v1.3.0 do ferramental de referência. Dentro da linha, as mudanças de schema são apenas aditivas e o $id permanece em v1.0; qualquer mudança incompatível é publicada como uma nova linha, nunca no lugar.
Os schemas#
- Cada schema carrega um
$idestável no formatohttps://leji.org/schemas/v<major>.<minor>/<name>.schema.json. A linha do$idsó se move quando o formato do schema muda de forma incompatível. - Dentro de uma linha publicada, as mudanças de schema MUST ser aditivas (novos campos opcionais). Remoções de campo ou mudanças semânticas exigem uma nova linha.
- Os artefatos legíveis por máquina que não sejam o manifesto declaram a linha de schema contra a qual foram escritos por meio de
schemaVersion; o manifesto declara a linha de especificação que tem como alvo por meio da chave autonomeadoraleji(item 2).
Conjunto de estabilidade#
Os itens a seguir são congelados dentro de uma linha de especificação; o ferramental (inclusive futuras implementações comerciais) é construído sobre eles sem nenhum schema paralelo:
- o formato do manifesto e seu nome de arquivo fixo
leji.json, - os identificadores de categoria (
domain,system,practice,governance,decisions), - os identificadores de nível de conformidade (
core,indexed,governed,federated), - as regras de normalização de identificadores e caminhos conforme machine-readable-surface.md,
- os formatos da entrada de índice, da entrada de changelog, do perfil de agente e do registro de decisão.
Ferramental de implementação (não normativo)#
Os SDKs e as CLIs seguem seu próprio SemVer e declaram com quais linhas da especificação são compatíveis. Neste repositório, os SDKs de referência são o pacote npm @leji-org/leji (packages/sdk), o pacote PyPI leji (packages/sdk-py) e o módulo Go leji (packages/sdk-go, um único binário estático); todos têm o mesmo comportamento e são testados com um único conjunto compartilhado de fixtures.