spec 1.0 · normativo

La superficie legible por máquinas

Cinco artefactos permiten que el utillaje lea la capa de contexto. El resto es prosa destinada a personas que los agentes también consultan; estos cinco artefactos constituyen el contrato sobre el que se construyen las herramientas.

Artefacto Ubicación por defecto Esquema
Manifiesto leji.json (raíz del repositorio, fija) context-manifest.schema.json
Índice del contexto <root>/context-index.json context-index.schema.json
Registro de cambios del contexto <root>/context-changelog.json context-changelog.schema.json
Perfiles de agente <root>/agents/*.md (frontmatter) agent-profile.schema.json
Registros de decisión <root>/decisions/*.md (frontmatter) decision-record.schema.json

Todas las ubicaciones salvo la del manifiesto se declaran en el manifiesto; la tabla muestra los valores por defecto.

Requisitos#

  1. Manifiesto. leji.json MUST existir en la raíz del repositorio y validar frente a su esquema. Es el único nombre de archivo fijo de Leji: el archivo que el utillaje busca de forma fiable.
  2. Índice. Una capa de contexto que declare conformidad indexed o superior MUST llevar un índice del contexto generado, nunca mantenido a mano: el utillaje resuelve los archivos de índice de categoría (categories.<id>.indexes, según content-categories.md) a los documentos que enumeran y escribe una entrada por documento gobernado. Cada entrada lleva un id estable, una path, un title y un identificador de category. Un generador SHOULD emitir también la clase (kind) del documento (intent o record); es opcional en el esquema para que un índice escrito antes de que existieran las clases siga siendo válido, y un consumidor trata un valor ausente como intent. La entrada de un registro lleva además su date cuando el documento declara una date válida en el frontmatter; el generador toma las fechas únicamente del frontmatter, nunca de la prosa ni de convenciones de nombres de archivo. Un índice desactualizado (uno que ya no coincide con lo que resuelven los archivos de índice) MUST tratarse como un fallo de validación. Una anfitriona que declara federation.mounts lleva además, en ese mismo índice, un array mounts de primer nivel: un registro de enrutamiento por montaje (name, source, pin, trackingRef cuando se declara, owner, role cuando se declara, y los metadatos de enrutamiento categories / topics / requiredWhen). Son únicamente registros de enrutamiento; el utillaje MUST NOT copiar las entradas ni la prosa de una hermana al índice de la anfitriona, y un registro de montaje no lleva nada que la audiencia de la anfitriona no pueda ver (según distribution.md, Montajes restringidos).
  3. Registro de cambios. Una capa de contexto que declare conformidad indexed o superior MUST llevar un registro de cambios legible por máquinas con los cambios de la capa de contexto. Las entradas llevan un id estable, una date en UTC, un type, un summary de una línea y las paths afectadas. El orden canónico es derivado, no posicional: el utillaje MUST ordenar las entradas por (date, id) ascendente, y la posición en el array no significa nada. Como el id es único dentro del registro de cambios (Identificadores), (date, id) es un orden total incluso cuando dos cambios comparten date. Las entradas supervivientes son inmutables: el utillaje MUST tratar la modificación de una entrada publicada como un fallo de validación allí donde pueda establecer el estado anterior, y reordenar el array no es una modificación. Establecer ese estado exige una base de comparación distinta; donde el utillaje de referencia solo dispone de la revisión actual, como en una copia ordinaria de integración continua, la modificación no le resulta visible y es la revisión del conjunto de cambios la que la detecta (véase conformance.md). El registro de cambios es una superficie de actualidad, no un archivo histórico: una capa de contexto longeva SHOULD compactarlo en vez de dejar que crezca sin límite, y MAY compactarlo en cualquier momento eliminando entradas por el extremo más antiguo de ese orden, siempre que el mismo conjunto de cambios anexe una entrada de tipo compaction cuyo campo compacted registre el recuento y el primer y el último id eliminados. Eliminar algo que no sean las entradas más antiguas, eliminar sin una entrada de compactación y compactar hasta dejar el archivo vacío son fallos de validación. La disciplina de solo anexión se indexa por conjunto según el id y se comprueba contra el estado previo confirmado, así que necesita git en el momento de escribir; el archivo en sí se mantiene libre de git para los consumidores, y el historial de git guarda el registro completo. Un conjunto de cambios que toca documentos gobernados (aquellos a los que resuelven los archivos de índice de categoría) MUST anexar una entrada cuyas paths cubran las rutas gobernadas que cambió, de modo que cada documento gobernado modificado quede bajo alguna entrada anexada: la disciplina de solo anexión mantiene inmutables las entradas publicadas, y esta regla de cobertura mantiene completo el registro. MAY existir junto a él un registro de cambios legible por personas; el registro JSON es el que lee el utillaje.
  4. Artefactos con frontmatter. Los perfiles de agente y los registros de decisión son documentos markdown cuyo frontmatter YAML valida frente a sus esquemas. El cuerpo en prosa queda libre; el frontmatter es el contrato con la máquina. MUST NOT exigirse perfiles o decisiones en JSON puro: estos documentos los leen personas.
  5. Identificadores. Todos los valores de id MUST ser estables una vez publicados: los renombrados y los movimientos actualizan la path, nunca el id. Los identificadores van en minúsculas, separados por guiones y son únicos dentro de su tipo de artefacto. El id de una entrada de índice generada se deriva en este orden de prioridad: el id del frontmatter del documento si lo declara; si no, el id que el índice almacenado ya lleve para esa misma ruta o, en un movimiento puro que preserva el contenido, para ese mismo contenido; si no, un slug del nombre de archivo, desambiguado frente a su directorio padre. Gana el primero que exista, de modo que un id publicado sobrevive a un renombrado o a un movimiento y solo un documento completamente nuevo acuña uno fresco. Un documento que pudiera moverse y editarse en un mismo conjunto de cambios SHOULD declarar un id en el frontmatter: solo el frontmatter fija el id a través de un cambio simultáneo de ruta y contenido (los mecanismos de reserva por ruta y por hash fallan ambos), y el utillaje avisa (id-vanished) cuando desaparece un id almacenado, de modo que se detectan las referencias colgantes que deja.
  6. Marcas de tiempo. Los valores de date del registro de cambios son ISO 8601 en UTC: o bien una fecha de calendario YYYY-MM-DD (ordenada como el inicio de ese día, T00:00:00Z), o bien una marca de tiempo de segundos enteros terminada en Z (por ejemplo 2026-06-13T15:04:05Z). Las horas sin zona, los desplazamientos distintos de UTC y las fracciones de segundo no están permitidos: las fracciones de segundo romperían la garantía de que una ordenación lexicográfica de date es una ordenación cronológica, ya que …05.1Z se ordena antes que …05Z siendo posterior. Todo campo de fecha de todo artefacto tiene rango de calendario, así que un mes 13 o un día 99 es inválido. Las fechas de los demás artefactos siguen ISO 8601 y MAY ser solo de fecha. Las rutas son de estilo POSIX, relativas a la raíz del repositorio, sin ./ inicial.
  7. Todo artefacto JSON salvo el manifiesto MUST declarar la línea de esquema contra la que se escribió (schemaVersion), según versioning.md; el manifiesto declara la línea de especificación a la que apunta con la clave autonombrada leji.
  8. Las superficies derivadas heredan las restricciones de acceso. El índice, el registro de cambios, el visor generado y cualquier vista compilada o exportada construida a partir del contenido de la capa de contexto son superficies derivadas, igual que lo es la salida que un agente produce a partir de ese contenido. Una superficie derivada lleva las restricciones de acceso del contenido más restringido del que se nutre. Una superficie derivada MUST NOT escribirse ni copiarse a un lugar con una audiencia más amplia que la de ese contenido sin un paso explícito y revisado de expurgo del contenido restringido que produzca una superficie separada para esa audiencia, y un agente MUST NOT citar ni resumir contexto restringido en una superficie de audiencia más amplia o menos restringida (un pull request, un ticket, un chat, un mensaje de commit o una capa de contexto pública). El índice de una capa de contexto restringida puede ser tan sensible como su prosa: los títulos, las rutas y los resúmenes la describen. Esto es una restricción sobre las personas y los agentes que operan el utillaje, no una comprobación que el utillaje realice: Leji no define ningún modelo de audiencia que una herramienta pueda leer para calcular qué es «audiencia más amplia» (el acceso corresponde al sistema de control de versiones, según governance.md, Límite de acceso), de modo que el SDK de referencia no lo aplica y lo más que hace cualquier herramienta es avisar (la exportación del visor avisa de que se aloje en privado).

Enrutamiento por tarea#

El índice, las asignaciones de categoría y los registros de decisión existen para que un agente pueda cargar la porción de contexto que una tarea necesita en vez del árbol entero. Esta sección define, de forma normativa, cómo el alcance de una tarea selecciona esa porción. Es el único algoritmo de enrutamiento al que se refiere el resto de la especificación: la sección de carga del perfil de arranque (boot-profile.md) remite aquí al agente en lenguaje de tarea, el alcance de los registros de decisión (decisions.md) coincide con él, y la lectura federada (distribution.md) lo reutiliza para decidir qué hermanas toca una tarea. El enrutamiento lee contexto; no es un sobre de tarea ni un protocolo de ejecución, que quedan fuera de la 1.0 (véase README.md, Límite de extensión).

  1. Entrada. El alcance de una tarea es el conjunto de rutas POSIX relativas a la raíz del repositorio que la tarea lee o cambia (normalizadas según el requisito 6: estilo POSIX, relativas a la raíz, sin ./ inicial), junto con las categorías que la tarea nombre explícitamente y los temas que la tarea nombre explícitamente. Los temas son entradas explícitas: el algoritmo nunca los deriva de rutas, categorías, prosa o contenido. Cómo deriva un agente o una herramienta el alcance a partir de la tarea queda fuera del alcance normativo; la comparación que sigue, no.
  2. Coincidencia de rutas (léxica, bidireccional). Una ruta declarada y una ruta de tarea coinciden cuando, tras la normalización (estilo POSIX, relativa a la raíz, sin ./ inicial, eliminada cualquier / final), las dos cadenas son iguales, o una es prefijo ancestro de la otra: la más corta es igual a la más larga truncada en un límite /. La comparación es puramente léxica: nunca consulta el sistema de archivos y no distingue entre una ruta que nombra un archivo y una que nombra un directorio, porque tras la normalización ambas son indistinguibles. Es contención en cualquiera de las dos direcciones (la relación underPath que comparten las implementaciones de referencia), de modo que una tarea de alcance amplio y un selector declarado de forma estrecha se encuentran sea cual sea el lado más amplio.
  3. Coincidencia de categorías (estrecha), y los dos conjuntos de categorías. Las categorías de una tarea se dividen en expandidas y señalizadas. Una categoría que la tarea nombra explícitamente entra en ambos conjuntos. Una ruta de tarea que es en sí misma un documento gobernado (que coincide con su entrada de índice generada por igualdad exacta, nunca por contención) aporta la categoría de esa entrada únicamente al conjunto señalizado. Las categorías expandidas cargan sus documentos de intención y sus registros candidatos; las categorías señalizadas son una señal de coincidencia para las decisiones y los montajes de federación y no cargan nada por sí mismas. Un selector de categoría MUST NOT inferir una categoría para un archivo cualquiera del repositorio, y una ruta de tarea que no sea en sí misma un documento gobernado, incluido cualquier directorio ancestro de uno, no aporta categoría alguna. El alcance por rutas llega a los archivos; la expansión por categoría no lo sigue.
  4. Coincidencia de temas (exacta, solo montajes). Un tema es una cadena no vacía de valores escalares Unicode, comparada por su codificación UTF-8; un sustituto suelto no es un tema válido. Ambos lados están sujetos a esa regla: un tema de tarea o una entrada de topics de un montaje que no sea una cadena no vacía de valores escalares Unicode es un error de entrada, y una implementación MUST rechazarlo en vez de devolverlo como una no coincidencia silenciosa. Un tema de tarea coincide con un tema declarado cuando las dos cadenas decodificadas son exactamente iguales. Las implementaciones MUST NOT cambiar mayúsculas y minúsculas, normalizar Unicode, comparar según la configuración regional, recortar, tokenizar, buscar subcadenas ni hacer coincidencias aproximadas en ninguno de los dos lados, de modo que grafías canónicamente equivalentes que difieren en bytes no coinciden; esta regla de igualdad es independiente del orden por bytes de los resultados que se define más abajo. Los temas de tarea duplicados forman una sola señal, así que nombrar un tema dos veces coincide exactamente igual que nombrarlo una vez. Un montaje de federación coincide cuando cualquier tema de la tarea es igual a cualquier tema que el montaje declare. Una coincidencia por tema selecciona únicamente el montaje: MUST NOT entrar en los conjuntos de categorías expandidas o señalizadas, ni cargar ningún documento o registro, ni enrutar ninguna decisión, ni evaluar requiredWhen, ni hacer que un montaje sea requerido.
  5. Filtro de estado. Solo se enruta como guía vigente un registro de decisión cuyo status es vinculante. accepted y deprecated son vinculantes; un registro deprecated es vinculante con una postura de desactualización, y un agente MUST tratarlo como guía en retirada más que como práctica vigente asentada. Un registro superseded MUST NOT ser vinculante salvo como historia y MUST llevar supersededBy; los registros proposed y rejected MUST NOT ser vinculantes. Un registro vinculante está vivo.
  6. Decisiones sin alcance. Un registro de decisión vivo que no declara ni affectedPaths ni affectedCategories es de alcance organizativo: se enruta para toda tarea, sea cual sea su alcance. Las decisiones vivas con alcance se enrutan solo cuando la tarea coincide con ellas por ruta (2) o por categoría (3).
  7. Alcance de rutas vacío, y alcance vacío. Cuando el conjunto de rutas de la tarea está vacío, la coincidencia de rutas no aporta nada y un agente MUST declarar que no se evaluó el enrutamiento por rutas; las categorías nombradas explícitamente se siguen honrando y siguen expandiendo, y los temas nombrados explícitamente se siguen comparando. Tanto las categorías nombradas como los temas nombrados cuentan como alcance no vacío. El alcance completo de la tarea está vacío solo cuando no nombra rutas, ni categorías, ni temas; entonces un agente enruta únicamente el contexto incondicional del perfil de arranque y del perfil de agente, más las decisiones vivas sin alcance de ámbito organizativo. Un agente MUST NOT presentar una carga no enrutada como si estuviera acotada.
  8. Los registros se enrutan como candidatos. Una categoría expandida enruta sus documentos de intención como contexto requerido; los registros de esa categoría se devuelven aparte, cada uno con su clase y su fecha, como candidatos que el lector carga a criterio propio. Un registro pasa a ser requerido solo cuando las rutas de la tarea lo seleccionan directamente según el punto 2, cuando un agente o el perfil de arranque lo nombran, o cuando una persona lo pide; ser una coincidencia de categoría, o llevar la fecha más reciente, nunca hace que un registro sea requerido. Cuando un registro es a la vez candidato por categoría y seleccionado directamente por ruta, gana la selección directa y es requerido. El enrutamiento MUST NOT certificar ningún registro como «el último» o «el vigente»: la 1.0 no define identidad de serie de registros ni garantía de orden, así que los juicios de actualidad corresponden al lector, hechos contra las fechas que expone el índice. Los registros de decisión conservan su propio enrutamiento (puntos 5 y 6) y nunca se enrutan como registros genéricos. Nombrar un archivo de decisión como ruta de tarea no enruta esa decisión; lo hace el alcance que ella declara.
  9. Cita. Un agente que carga registros de decisión enrutados MUST citar qué registros coincidentes cargó, para que un lector pueda ver qué guía aplicó el agente e inferir cuál no.

La porción enrutada es lo que un agente carga para una tarea. Es la unión de: el conjunto de carga incondicional del perfil de arranque y el requiredRead del perfil de agente activo, que el agente sostiene como su línea base con independencia de cualquier alcance; todo documento gobernado de intención de una categoría expandida; toda entrada gobernada que las rutas de la tarea seleccionen según el punto 2; todo registro seleccionado directamente por ruta según el punto 8; y toda decisión viva con la que la tarea coincida por ruta o por una categoría señalizada o expandida, más las decisiones vivas sin alcance de ámbito organizativo.

Las categorías señalizadas aportan únicamente coincidencia, a las decisiones y a los montajes de federación, y nunca expanden un corpus.

La porción no es el sobre. Una herramienta que calcula el enrutamiento devuelve la porción junto a material que el agente no debe cargar sin que se le pida: los registros candidatos y los metadatos de candidatura que los rodean. Cargar el sobre entero desbarata el propósito del enrutamiento.

El orden de los resultados es normativo allí donde una herramienta emita uno, de modo que implementaciones independientes coincidan byte a byte: las categorías, en el orden canónico de categorías de esta especificación; los documentos, registros y decisiones, por ruta ascendente; los montajes, por nombre ascendente. La comparación de cadenas es por bytes sobre UTF-8, no dependiente de la configuración regional ni de la ordenación por puntos de código.

El utillaje MAY ofrecer un ayudante que calcule esta porción a partir de un conjunto de rutas; las implementaciones de referencia exponen uno (route). Ese ayudante calcula la parte dependiente del alcance y no está obligado a emitir la línea base, que quien lo invoca ya sostiene; la obligación del agente de cargar esa línea base no cambia. El enrutamiento es conforme siempre que un lector sin utillaje siga este algoritmo.

Notas (no normativo)#

El utillaje de referencia comprueba hoy el esquema del registro de cambios y su disciplina de solo anexión (leji validate ejecuta ambas); verificar la cobertura del registro de cambios contra una revisión base, es decir que toda ruta gobernada modificada aparezca en una entrada anexada, es una comprobación con informe que está en la hoja de ruta, todavía no una comprobación cuyo fallo impida continuar. Hasta que llegue, la cobertura se apoya en la disciplina de revisión y de integración continua atestiguada por proceso (véase conformance.md).

El índice es la fuente de navegación del contexto gobernado: leji viewer construye esa estructura gobernada a partir del índice y muestra debajo el árbol de directorios del repositorio como zona de referencia navegable. Así, una sola vista reúne el contexto gobernado y la navegación que el equipo ya utiliza. Cualquier herramienta de documentación puede proyectar el índice del mismo modo. La presentación no es normativa. La superficie es deliberadamente pequeña. Bastan cinco formas para que el utillaje valide una capa de contexto, la compare, puntúe su vigencia y encamine a un agente hacia la porción correcta; son lo bastante pocas como para que un equipo pueda tener presente toda la superficie. Todo lo que exceda esas cinco formas queda para después de la versión 1.0 y dependerá de la experiencia práctica.