spec 1.0 · 规范性

Leji 规范

本页按阅读顺序汇集了规范全部规范性文档的资料性译文,便于从头通读或在整页中检索。每一节都链接到各自的独立页面,其中每个标题都带有引用锚点。规范性文本以英文原文为准。

Leji 规范 ↗拉取请求

Leji 规范

Leji 是一份面向 AI 原生团队共享上下文层的开放规范。它定义团队如何存储、治理、加载和维护归属于代码仓库的上下文;人和 AI 智能体执行每项任务时,都会读取这份上下文。

规范版本 1.0.0
状态 已 GA,在 v1.3.0 参考工具发布时冻结。破坏性变更需要新的主版本。
编辑 Vuong Nguyen
单页版 整份规范汇于一页

原则(非规范性)#

  1. 意图重于指令。 Leji 记录持久的意图(事物意味着什么、必须满足哪些约束、为何如此),而不是针对特定厂商的命令式指令。人和智能体结合已声明的意图与任务上下文来推导行动。
  2. 是圆环,不是层级。人对人、人对 AI、人对 AI 再对人,这三种流动围绕同一个共享上下文层,都是一等公民。平等的是访问权,而不是决定权:凡能访问某个上下文层的人都读取它的全部内容,任何参与者都可以提议,由人来批准。参与身份基于角色而非工具:一位从不直接使用 git 的参与者,在这个圆环中同样是一等参与者。访问权本身由版本控制系统授予,而不是由 Leji 授予;圆环的范围就是一个上下文层的受众。
  3. 机制重于善意。共享上下文会自然失效:现实不断变化,文档却不会自行更新,wiki 也没有机制确保内容始终最新。Leji 依靠机制而非善意来强制维护:变更与代码经过同一道评审关卡,工具在检测到机械性漂移时报错,复核期限标记已经过时的内容,而过期上下文绝不会被悄悄当作当前有效(规范性表述见 governance.md 的“时效性”一节)。

本规范其余部分,都是这三条原则的规范性推论。

一致性用语#

本规范中的关键词 MUSTMUST NOTREQUIREDSHOULDSHOULD NOTRECOMMENDEDMAYOPTIONAL,按 RFC 2119 的描述解释。

本译文正文中的必须不得应当不应可以推荐,依次对应上述 MUSTMUST NOTSHOULDSHOULD NOTMAYRECOMMENDED,强度相同;REQUIREDMUST 同强度,OPTIONALMAY 同强度。这些中文词只是英文关键词的对照说明,规范性效力始终以英文原文为准。

引用本规范(非规范性)#

引用某一章节时,写明章节标题与规范版本,并附上指向该章节锚点的永久链接。在规范站点上,每个标题在悬停时都会显示自己的锚点。

  • 格式: Leji 1.0, §Sectionhttps://leji.org/spec/<document>/#<anchor>
  • 示例: Leji 1.0, §The circle, normatively:https://leji.org/spec/governance/#the-circle-normatively

务必写上版本号(Leji 1.0):破坏性变更以新的主版本发布,因此锁定版本的引用在规范演进之后依然准确。

词汇表#

以下术语在所有规范性文档中的用法保持一致:

术语 含义
context layer(上下文层) 本规范所治理的产物:一组归属于代码仓库、纳入版本管理的人类可读文档与机器可读产物,编码了一个团队持久的运作上下文。“Leji context layer”是消除歧义的完整写法。始终写“context layer”;单独的“layer”仅保留用于指称联邦中可数的层实例(同级层、宿主层、已挂载层、受限层、伴随层或不可访问的上下文层)。
agent(智能体) 会行动的 AI 系统:它加载仓库上下文,执行或协助工作,并且可以提议变更。这是规范中的行动者名词。
person / people(人) 人类参与者。批准权归人所有。
participant(参与者) 一个人或一个智能体。
audience(受众) 依据仓库权限,以及任何暴露该工作副本的文件系统或共享盘权限,被准许读取某个上下文层的人与智能体。“所有人都读取”的范围就是一个上下文层的受众;不同受众由彼此独立的上下文层服务,绝不通过在同一个层内部设限来实现。
agent host(智能体宿主) 智能体运行所依托的产品或运行时(例如 Claude Code、Codex、Cursor)。厂商适配器配置的就是智能体宿主。
tool(工具) 智能体可调用的能力(shell、搜索、某个 MCP 服务器)。绝不是产品名。
vendor adapter(厂商适配器) 智能体宿主的入口文件,它重定向到引导配置,本身绝不承载权威内容。有些适配器可在多个宿主间通用(AGENTS.md),有些只服务单一宿主(CLAUDE.md.cursor/rules)。两者规则相同;区别只体现在工具默认生成什么。
boot profile(引导配置) 上下文层中与智能体无关的入口,同时面向人和智能体。
agent profile(智能体配置) 面向智能体、按角色划分的加载与行为姿态文档。
AI 用作形容词(AI 原生),并出现在流动的名称中:人对人人对 AI人对 AI 再对人。在这些流动名称里,“AI”指的是通过智能体宿主运行的智能体。
model(模型) 智能体所运行的预测引擎。读取上下文层的是智能体,不是模型。这个词只在必须把引擎与行动者区分开时出现(例如模型选择这类宿主专有的机制)。

这套层级关系可以概括为:模型驱动智能体智能体通过智能体宿主运行并调用工具;上下文层面向智能体与宿主,而不直接面向模型。本规范对技术栈中的每一层都保持中立:任何模型都可以驱动任何智能体,通过任何宿主运行,并读取同一个上下文层。“LLM”有意不纳入这套词汇,因为它只代表模型中的一类,而本规范按同一原则对所有模型保持中立。

范围边界。 Leji 1.0 治理的是智能体,以及加载仓库上下文的智能体宿主。非智能体形态的 AI(自动补全、行内建议、不带仓库上下文的聊天)不在规范范围内,除非它作为某个加载上下文层的智能体宿主的一部分运行。

规范性文档#

按阅读顺序:

文档 定义内容
context-layer.md 上下文层、清单、上下文根目录、厂商适配器规则
content-categories.md 五个逻辑内容类别,以及索引文件如何把内容映射到类别
boot-profile.md 每个智能体宿主都会加载的、与智能体无关的入口
machine-readable-surface.md 清单、索引、变更日志、配置文档、决策记录
decisions.md 决策记录
governance.md 提议与批准、归属、纳入与移除、时效性
distribution.md 单体仓库、多仓库子模块、联邦
conformance.md 四个一致性级别与检查清单
versioning.md 规范与 schema 的版本管理

../schemas/ 中的 JSON Schema 对机器可读产物具有规范性。../rationale/../adoption/ 中的文档是非规范性的。

1.0 的范围#

范围之内:提供上下文、设定约束、记录决策、评审变更、沉淀可复用的模式;与智能体无关的接线方式与厂商适配器(浅度覆盖);归属与延续性语义(浅度覆盖)。

扩展边界。 Leji 1.0 规定的是权威的共享上下文层:团队上下文如何被书写、归属、纳入版本、提议、批准、索引与读取。它刻意规定围绕该上下文层运转的执行协议:任务信封、通用化的证据协议、智能体之间的交接、工具权限协议以及编排。这些是扩展协议,而非前提:一个符合 1.0 的上下文层在没有它们时必须依然可用,实现也不得要求具备它们才能读取、提议、评审、批准或校验上下文层。它们会在实践证明其价值时补全这门语言;它们不会被凭空发明出来。

Leji 不是编程语言、DSL、运行时,也不是 SaaS。它由 markdown 约定、精简的 JSON Schema 和治理语义构成。

上下文层 ↗拉取请求

上下文层

Leji 上下文层由一组纳入版本管理、受治理的人类可读文档构成,记录团队如何理解自己的工作:领域语言、系统不变量、约定、护栏和决策记录。人和智能体都会在实际工作中读取它,也会通过同一道评审关卡提议变更。上下文层存放在版本控制系统中,因此历史、时效与批准状态都可以验证;具体机制见下文的要求,参与者及其参与方式见参与方式

参与方式#

参与一个上下文层是基于角色的,而不是基于工具的。读取、提议、评审与批准,通过任何能保留仓库评审与批准语义的界面进行;参与要求懂得直接使用 git 或命令行。

  • 凡有访问权的人都读取。这里的访问权指的是通过团队日常工具获得的实际访问能力,而不是对仓库的 shell 访问权限。
  • 任何参与者都可以提议;由人来批准。提议是一次有意的上下文层变更请求。它可以由人直接撰写,可以由智能体依据某人的请求生成,也可以由智能体依据观察到的工作生成。实践中,多数上下文变更由智能体起草;不可替代的人类贡献是治理:提出意图,并批准什么可以成为权威内容。批准变更的人对其含义与后果负责,而不是对亲手操作版本控制系统负责。
  • 人类的含义,机器可读接口。人类可读的文档才是团队运作上下文的规范性来源。机器可读的文件(清单、索引、变更日志)的存在,是为了让工具能够定位、索引、校验并同步那份含义;它们绝不取代含义本身。

这些流动的规范形式,即圆环(所有人都读取,任何参与者都可以提议,由人来批准),定义在 governance.md 中。

读取一份记录#

受治理的内容分为两类,定义见 content-categories.md意图按当前真实状态持续维护;记录是带日期的证据,后续状态会取代它,而不是修正它。两者受到同等治理,区别在于读取者可以如何使用。读取者不得把记录当作当前意图:记录以证据的形式提供信息,其有效范围以自身声明的边界为限;如果读取者不加说明,便将记录中的主张表述为当前状态,就等于赋予这份文档本不具备的时效性。这与下文的降级模式规则原理相同:无论哪种情况,读取者都有义务了解并说明手中内容的时效性。

要求#

  1. 上下文层必须存放在一个 git 仓库中,并且必须与它所描述的工作一起纳入版本管理(同一个仓库,或者一个专用的上下文仓库,按 distribution.md 消费)。正是这个 git 仓库,使上下文层的历史、工作副本时效与仅可追加的变更日志完整性可被验证;符合规范的工具从中推导出这三者。在没有该仓库的情况下读取上下文层是受支持但降级的模式,定义见读取模式:权威与降级

  2. 接入 Leji 的仓库必须在仓库根目录携带清单文件 leji.json,且对 context-manifest.schema.json 有效。清单是机器入口:它声明规范版本(自命名的 leji 键)、上下文层名称、上下文根目录、引导配置路径、类别映射、可选的一致性声明,以及归属。它也可以携带一个 agents 映射,把角色标识符(例如 thought-partnerreviewer)绑定到智能体配置文档:协议启用角色,而这个映射决定由谁来担任。该映射是角色目录,不是加载顺序:任何绑定,包括 default 键上的绑定,都绝不会导致某份配置被读取;只有引导配置的“加载”一节才会。

  3. 清单可以声明 actors(可担任者):能够填充角色的具名参与者。每个 actor 声明它有资格担任的角色,以及按角色划分的命令模板。以角色为键正是关键所在:同一个 actor 可能因为担任的角色不同而需要不同的调用方式,因此“每个 actor 一条命令”无法表达这一点。一个 actor 声明的角色集合与它的命令键集合必须相同。当某个角色配有 actor 时,绑定到该角色的智能体配置不得同时声明 invocation:两条都自称权威、又没有说明优先级的命令,本身就是矛盾,而本层通过把命令声明在唯一一处来消解它。actors 是可选的,多数层不需要。它们的价值出现在某个角色有多于一个合格 actor 时,或者某个 actor 因担任角色不同而需要不同调用方式时;任一条件单独成立即足够,而一个只有单一 actor、只需一条命令的角色,由配置自身的 hostinvocation 即可满足。声明一个 actor 不授予任何批准权:它说的是谁可以被请来担任某个角色,绝不是谁可以批准。

    命令模板无论出现在哪里(actor 的 commands 值,以及智能体配置的 invocation.command),都遵循同一条规则。模板是一行命令,交给调用方所选的 shell;不是 shell 形态的启用方式(结构化的 argv 调用、进程内派生)在 1.0 版本系列中无法由这些字段表达。每个模板必须携带 <prompt> 占位符,且每一次出现必须作为独立的、未加引号的 shell 词出现在参数位置上,绝不能置于引号内或与其他文本拼接。替换是一次性的:所撰写模板中已出现的各处会被同时、且恰好各替换一次,因此提示文本内部字面的 <prompt> 字符串仍是数据,绝不会被二次展开。交付方式由调用方负责,而契约是所要求的结果而非引用转义算法:每一次出现都产生恰好一个参数,其值等于提示文本,且其中任何部分都不作为 shell 语法求值。Schema 所验证的是替换位点的存在;位置与交付则由本规则要求、由调用方履行。

  4. 清单必须声明一个上下文根目录(rootPath)。推荐的默认值是 docs/。上下文层的所有路径都是 POSIX 风格,相对于仓库根目录。rootPath 声明上下文层存放在哪里;它不会为它所治理的路径重设基准:索引条目、侧边栏固定页面、配置路径,以及任何 Leji 产物中的其他每一个路径,都从仓库根目录解析,包括那些重复了 rootPath 前缀的路径。查看器的 homepagelogofavicon 是例外:它们相对上下文根目录书写,落在该根目录之下的、相对仓库根目录的路径也会被接受。类别路径与 machine 路径应当落在 rootPath 之下;否则校验器会告警。

  5. 上下文层必须拥有一份引导配置,见 boot-profile.md推荐的默认位置是 docs/boot-profile.md;实际位置由清单的 bootProfilePath 声明。

  6. 上下文层的内容必须首先是人类可读的。散文格式推荐使用 markdown;结构化元数据使用 YAML frontmatter,或 machine-readable-surface.md 中定义的 JSON 产物。只有机器能读的文档不属于上下文层。

  7. 上下文层必须有一位具名负责人(清单中的 owners.primary):一个对其时效性负责的人。无人负责的上下文层必然腐烂。

读取模式:权威与降级#

上下文层有两种读取模式;读取者必须知道自己处于哪一种,因为二者的保证不同。读取者依据自己能解析到什么来判定模式:能在仓库根目录访问到 leji.json并且具备 git 工作树或宿主平台提供的仓库版本标识,即为权威模式;两者皆无、只能作为普通文件读到内容,即为降级模式。

  1. 权威模式。读取者通过 git 仓库解析上下文层:一份工作副本,或宿主平台的仓库视图。历史、工作副本时效与变更日志完整性都可被验证,已批准的内容可确知对应到所读取的版本。
  2. 降级模式。读取者以普通文件内容的形式接触上下文层,没有可用的 git 工作树或版本元数据:文件被上传、同步或复制到另一个界面,而仓库并未随行。普通文件读取就阅读而言是一等的(文档按要求本就人类可读,机器可读的变更日志仍传达声明的新近程度),但降级模式的读取者必须把工作副本时效与批准状态视为未知,绝不能视为当前有效(见 governance.md 的“时效性”)。在此模式下,变更日志是可随副本携带、用于声明新近程度的信息载体;它本身并不能证明这份副本与权威仓库一致。

降级读取扩大了谁能消费上下文层、以及能消费到什么;它绝不是通往权威性的路径。变更只有经由基于 git 的评审关卡才成为权威,而降级副本无法满足联邦所依赖的权威模式检查(固定版本的时效、过期固定版本状态、归属完整性,以及受限挂载的访问检查,见 distribution.md)。

厂商适配器规则#

智能体宿主的配置文件(例如 CLAUDE.mdAGENTS.mdGEMINI.md.cursorrules.cursor/rules.windsurfrules.github/copilot-instructions.md):

  1. 不得承载权威的上下文层内容。
  2. 若存在,必须重定向到引导配置(通常是一行指针)。
  3. 可以携带在该智能体宿主之外没有意义的宿主专有机制(模型选择、运行器设置),前提是其中不存放任何团队知识。
  4. 工具从两个来源发现需要检查哪些入口文件:清单中可选的 vendorAdapters 列表,以及一份公开的知名集合(上面列出的那些文件)。上面的示例列表就是本版本系列的知名集合;入口文件不在其中的宿主,只有在清单于 vendorAdapters 中点名时才会被检查。

现有的入口约定告诉智能体宿主去哪里看;Leji 定义的是智能体在那里会看到什么。一个事实来源,所有参与者读的都是它。

上下文层不是什么(非规范性)#

  • 不是 wiki。 Wiki 本身没有机制确保内容始终最新。上下文层能够持续有效,是因为智能体执行每项任务时都会读取它(错误的上下文会立即导致可感知的错误结果),变更像代码一样经过评审,工具也会让陈旧内容显现出来。
  • 不是传统意义上的文档。传统文档往往在事后描述系统做了什么;上下文层则用现在时描述团队如何思考,并持续供人和智能体读取。
  • 不是可直接导入的模板。借来的上下文很快就会过时。上下文层的价值在于它表达的是团队自身的认识;Leji 标准化的是形态与治理,而不是内容。

内容类别 ↗拉取请求

内容类别

Leji 定义了五个逻辑内容类别。分类依据是文档为何存在,而不是存放位置:类别名称是供清单、索引和工具使用的稳定标识符,目录名则由团队自行决定。

五个类别#

类别 属于它的内容
domain 用团队自己的话表达的业务语言与产品语义:核心名词是什么意思、它们之间如何关联、哪些词带有本地含义。业务状态类记录(某项合作的状态、一份市场快照)也归入这里,作为记录。
system 架构及其不变量:服务边界、数据归属、集成契约、一致性模型、失败契约,以及每一次变更都要遵守的约束。技术评估与系统读数归入这里,作为记录。
practice 会被自动套用的约定与模式:代码约定、测试模式,以及已被验证有效的提示词与工作流模式(见下文的沉淀门槛)。方法执行过程的记录(一次复盘、一份操作手册执行日志)归入这里,作为记录。
governance 智能体护栏与运作规则:智能体在无人提示时可以做什么、什么需要人来把关、数据处理规则、上报触发条件、合规控制。治理证据(审计日志、评审报告)归入这里,作为记录。
decisions 带日期的记录,说明事情为何如此,见 decisions.md

意图与记录#

每一份受治理的文档,要么是意图,要么是记录,与它属于哪个类别无关:

  • 意图是持续维护的当下真实:术语表、不变量、约定、护栏。读者依赖它是当前有效的,因此当现实发生变化时,文档要被修正。复核期限与时效机制存在的意义,正是为了意图(见 governance.md)。
  • 记录在一个明确的时间或事件边界内保存主张:状态、评估、台账、读数、会议结论、归档。后续状态取代记录,而不是修正它;原件仍然是关于它那个时点的有效陈述。记录以其日期标示时效,而不是以复核期限标示。

判断分类时,只需问一个问题:*如果后续信息与这份文档不一致,是因为读者依赖它当前有效,所以必须修正文档;还是因为新信息取代了它,而原文在当时依然成立?*前者是意图,后者是记录。

记录受到与意图完全相同的治理:被索引、被评审、有归属、被路由。区别在于读者可以拿它做什么:读者不得把记录当作当前意图;它是带日期的证据(见 context-layer.md 的“读取一份记录”)。决策记录是记录的正式子类型:它们本质上就是记录,并拥有自己的 schema 与生命周期,见 decisions.md

关于记录的一些问题被刻意排除在 1.0 之外,且被公开承认而非掩盖:不存在机器层面的记录系列概念(因此工具绝不会认定哪一份记录是“最新的”),没有流式新近度机制(下一份应有的记录是否已经逾期),也没有针对实质上混合了意图与记录内容的文档的章节级类型。混合文档应当拆分;在拆分代价过高时,按下游读者主要依赖的契约来分类。确实无法归入任何类别的内容留作参考资料;分类并不承诺免于判断。

要求#

  1. 清单必须把它声明的每个类别映射到一个或多个相对仓库根目录的索引文件categories.<id>.indexes);每个索引文件应当落在所声明的上下文根目录之下,见 context-layer.md。索引文件声明的是纳入,而不是搬迁:内容仍留在团队原本存放的地方(例如 business/technology/architecture/),并且同一个目录可以在不做任何重命名的前提下,向多个类别贡献文档。
  2. 索引文件是经过整理的 markdown,其中携带一个或多个 leji-index 围栏代码块。一个块以三个或更多反引号加上该块的信息串的一行开启,并由下一行三个或更多反引号关闭;关闭围栏的反引号数量不必与开启围栏一致。合法的信息串恰好有三种:leji-index(意图块)、leji-index intent(同上,显式写法),以及 leji-index record(记录块,其条目解析为记录)。leji-index 之后出现任何其他 token 都是解析错误,绝不会被悄悄忽略:这套文法在设计上是有限的。每个块以每行一条的方式列出内容,形如 - path: <相对仓库根目录的路径>,其中路径可以是一个目录(其 markdown 被递归纳入)或单个 markdown 文件。路径必须是相对仓库根目录的 POSIX 路径:前导 /.. 片段或反斜杠都是非法的,会被拒绝。空行与整行 # 注释被忽略,条目可以在其后携带一个由空白引出的 # 注释。这套文法中的空白只有 ASCII 空格(U+0020)与制表符(U+0009),别无其他,在文法所有需要判定空白的位置都是如此:围栏反引号与信息串周围、条目行的前导与尾随填充,以及引出尾随注释的 # 之前。解析前会先剥除前导的 UTF-8 字节序标记。行按 LF 切分,容忍尾随的 CR,文件为 UTF-8。实现不得在此处使用运行时的空白字符类:任何被某个运行时恰好归为空白的其他字符,包括 U+0085 与 U+00A0,都是普通的路径内容,因此携带这类字符的条目会被报告为路径缺失,而不是被悄悄裁剪。boot-profile.mdleji-mounts 块冻结在同一套字符集上,因此同一个扫描器可以读取两套文法,三个实现也不会对“这里到底有没有围栏”产生分歧。同一文件中的多个块按文档顺序拼接。块前后允许出现散文与标题,因此索引文件同时也是该类别的人类可读地图。扫描是按行进行的,不考察 markdown 结构:一行若在可选的空格或制表符缩进之后带有三个或更多反引号与该标签,无论它位于文档何处,都会开启一个真实的块,包括位于更长的围栏示例内部或列表项内部。因此,只用于举例而非声明的示例要用不同的标签围栏,绝不能在 leji-index 之后多加一个 token:扫描器匹配的正是标签,所以 leji-index example 会开启一个真实的块并报出解析错误,而标签为 text 的围栏什么也不会开启。推荐位置是上下文根目录下的 context/<id>.md;该位置可配置,工具绝不将其写死。
  3. 上下文层必须至少映射 domainsystem,再加上 decisions,才能声明任何一致性级别(见 conformance.md),并且已填充的 domain/system 最小集合必须至少包含一份意图文档:只由记录构成的上下文层保存了历史,却不承载任何运作上下文。其余类别随团队遇到真实问题而逐步积累;不得为了满足检查清单而映射一个空类别(其索引文件解析不出任何文档)。
  4. 一份文档解析到恰好一个类别与一种类型。索引条目是选择器,解析遵循选择器特定性:直接的文件选择器胜过任何目录选择器,更深的目录选择器胜过其祖先目录选择器。覆盖某份文档的最具体选择器决定它的类别与它所在块的类型;一份文档若被较宽泛的选择器覆盖、却由更具体的选择器胜出,那它就根本不属于那个宽泛选择器的内容(记录目录中某个需要保持最新的文件,或某个更大映射树里某个团队自己的决策日志,正是这样在不搬动任何东西的前提下表达出来的)。同等特定性的选择器若对类别或类型意见不一,是错误,绝不按索引顺序来裁决;同等特定性下相同的赋值只解析一次,而同一个索引文件内字面重复的条目会被拒绝。工具应当提示那种“所覆盖的每份文档都被更具体选择器夺走”的选择器(被遮蔽的选择器):那是这张整理过的地图上的冗余,但绝不是错误。除此之外解析是确定性的:目录条目按 POSIX 字典序(依 Unicode 码点;推荐路径保持 ASCII,使顺序在各实现间无歧义)展开为其中的 markdown,而任何真实位置(解析符号链接之后)逃出仓库根目录的路径会被排除,而不是被跟随。索引条目(见 machine-readable-surface.md)携带类别标识符与类型。
  5. 文档可以在 frontmatter 中声明自己的类型(kind: intentkind: record);frontmatter 覆盖胜出选择器所在块的类型,绝不覆盖类别。其他任何 kind 值都是错误。决策记录不带 kind 键(它们的 schema 是封闭的,且本质上就是记录)。记录可以携带 frontmatter 中的 dateYYYY-MM-DD);工具从该字段读取记录的日期,绝不从散文、标题约定或文件名读取。记录不得携带 freshness.reviewAfter(复核期限是意图的机制;出现在记录上就是在承诺一份文档不可能具备的时效性,这是错误)。
  6. 描述提示词或工作流模式的 practice 内容应当在该模式至少奏效两次之后再沉淀(“两次验证”门槛)。过早沉淀正是 practice 目录被愿景填满的原因。

注记(非规范性)#

并非每个类别从第一天起就要存在。最小可用的上下文层,只需包含最初一个月的工作真正依赖的内容。设置类别,是为了让人或智能体能够判断“这属于哪一种真实”,进而只加载当前任务所需的内容,而不是整棵文档树。

之所以区分这两种类型,是因为真实仓库的文档通常由两套相互交织、真值模型不同的语料组成。若将偏运营的部分强行归入意图语义,只会导致两种结果:要么作出无法兑现的时效承诺,要么将仓库中的大量内容排除在治理之外。下面是一个完整示例,其中在记录目录内保留了一项意图例外:

# Domain context

```leji-index
- path: docs/glossary.md
```

Operational state is governed as records; the escalation policy stays intent.

```leji-index record
- path: docs/operations/
```

```leji-index intent
- path: docs/operations/escalation-policy.md
```

引导配置 ↗拉取请求

引导配置

引导配置是上下文层中与智能体无关的入口。它是一份人类可读的文档,任何智能体宿主和任何人都可以从这里开始,并回答三个问题:“这个上下文层是什么?我该加载什么?我在这里该如何行事?”

要求#

  1. 上下文层必须恰好有一份引导配置,位于清单中 bootProfilePath 所声明的路径。推荐的默认值是 docs/boot-profile.md

  2. 引导配置必须是纯 markdown,任何人不借助工具也能读懂。它不得依赖任何厂商的配置语法。

  3. 引导配置必须涵盖:

    • 身份:这个仓库或产品是什么,一段话说清。
    • 加载:什么样的任务该读什么上下文。这里必须给出一个无条件集合(任何任务之前都要读的内容),然后是按任务类型划分的选择器,依路径、依类别或经由上下文索引来路由,并为匹配不到任何选择器的任务定义兜底方案。用任务语言表述的这一节,就是“任务路由”算法(machine-readable-surface.md)在引导配置层面的表达;照做无需了解那个算法。
    • 姿态:对智能体运作方式的预期(何时可以直接推进、何时必须发问、绝不能做什么)。这可以通过引用治理内容或某份核心智能体配置来承载。
  4. 引导配置应当链接到清单、索引(若存在)与各智能体配置(若存在),使得从任何宿主进入的智能体都能发现完整的机器可读接口。

  5. 引导配置必须讲任务语言:它写明字面路径与具体加载顺序,照做无需了解本规范。清单与 schema 是为工具而存在的,不是为智能体;一份需要读懂规范才能照做的引导配置,是一致性上的坏味道。

  6. 引导配置应当说明上下文层的维护职责:它的变更记录在哪里(所声明的变更日志),以及决策如何沉淀(所声明的决策记录位置)。若引导配置两者都未提及,校验器会告警。

  7. 厂商入口文件按 context-layer.md 中的厂商适配器规则重定向到引导配置。

  8. 引导配置的无条件加载集合(它声明在任何任务之前都要读的内容)应当限定在每项任务都真正需要的范围内。只有部分任务需要的上下文应当按任务、按类别或经由索引来路由,而不是预先加载;决策记录应当按其声明的 affectedPaths / affectedCategories 来路由,而不是整个目录一次性加载,因为它们会无止境地累积。无条件集合中的每一样东西,在每项任务上都要付出代价。

  9. 联邦同级层。 声明了 federation.mounts 的上下文层(见 distribution.md必须在引导配置中以机器可检查的形式呈现这些同级层:一个或多个信息串为 leji-mounts 的围栏块,可置于文档任意位置,其条目按文档顺序拼接,且每个已声明的挂载恰好对应一条条目。一条条目写明该同级层、它的负责人、它承载什么,以及何时该读它,后两项用作者自己的任务语言表达。成型的示例见要求之后。

    文法是固定的,以便每个实现读法完全一致。一个块以三个或更多反引号加上信息串的一行开启,并由下一行三个或更多反引号关闭;关闭围栏的反引号数量不必与开启围栏一致。信息串只能是 leji-mounts 本身;其后携带任何 token 的围栏都是错误,绝不是被忽略的围栏。围栏行可以带有空格或制表符的缩进与填充,而其间的记录不得如此:一条记录从第 1 列的 - mount: 开始,其字段恰好缩进两个 ASCII 空格。一条记录内部,ownercarriesread-when 各出现恰好一次,顺序不限;未知字段、重复字段与缺失字段都是错误。值是 key: 前缀之后该行剩余的非空部分,不带前导或尾随的空格与制表符,也不含任何控制字符或行分隔字符。这套文法中的空白只有 ASCII 空格(U+0020)与制表符(U+0009),别无其他,在围栏行的缩进与填充中如此,在内容行中亦然;实现不得在此处使用运行时的空白字符类,因为各运行时对 U+0085 与 U+00A0 这类字符的归类并不一致,进而会对“块是否存在”产生分歧。解析前会先剥除前导的 UTF-8 字节序标记。行按 LF 切分,容忍尾随的 CR,空行与以 # 开头的整行被忽略(与 content-categories.md 的类别索引块相同),文件为 UTF-8。扫描是按行进行的,不考察 markdown 结构:一行若在可选的空格或制表符缩进之后带有三个或更多反引号与该标签,无论它位于文档何处,都会开启一个真实的块,包括位于更长的围栏示例内部或列表项内部。因此,只用于举例而非声明的示例要用不同的标签围栏,绝不能在 leji-mounts 之后多加一个 token:扫描器匹配的正是标签,所以 leji-mounts example 会开启一个真实的块并报出解析错误,而标签为 text 的围栏什么也不会开启。mount 必须与某个已声明挂载的 name 一致,owner 必须与该挂载所声明的 owner.name 一致,按解码后的字符串比较;为未声明的挂载写条目、为同一个挂载写第二条条目、以及已声明却没有条目的挂载,都是错误。未声明任何挂载的层不得携带 leji-mounts 块。

    同级层的位置被刻意排除在字段之外:挂载会被物化到一个机器本地、按内容寻址的投影中,因此读取者用 leji mounts locate <name> 来解析它,而不是去推断路径(见 distribution.md)。块周围的散文应当自然地解释路由方式;块是可检查的内核,绝不是那段散文的替代品,也不是 leji.json 中声明的替代品。已挂载的同级层是彼此独立的具名来源,绝不并入宿主层的类别;只有当任务匹配某个同级层的路由,或配置本身要求时,引导配置才把智能体导入该同级层。哪些内容不作检查也是刻意的:carriesread-when 是自由文本,它们与挂载路由元数据的吻合程度由团队自行担保,而不由工具验证;工具检查的是枚举、身份与存在性。在这里呈现同级层,使挂载的发现留在智能体的任务语言入口中,因此照做要求 5 时依然无需阅读清单。

一份成型的 leji-mounts#

一条条目,对应一个声明了名为 acme-product-context 的单一挂载的宿主层。该块在引导配置中位于第 1 列,就是这里读到的样子;外层的四反引号围栏是本文档的包裹,并不属于它。

```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
```

智能体配置#

上下文层可以machine.agentProfilesPath 所声明的目录下定义按角色划分的配置(例如一份 reviewer 配置、一份 release 配置、一份 QA 配置)。每份配置:

  1. 必须是带 YAML frontmatter 的 markdown,且 frontmatter 对 agent-profile.schema.json 有效。

  2. 在继承解析完成后,必须携带该角色首先读取什么(requiredRead),以及它必须停下来发问的情形(mustAskWhen)。声明了 inherits 的配置,可以在其基配置已提供某一项时省略该项;未声明 inherits 的配置必须自行声明两者。

  3. 可以声明 inherits,它在 1.0 版本系列中是生效的:它指向本层配置集合中恰好另一份配置,该配置的 role 必须core,本配置扩展它的姿态与正文。本层的配置集合,是所声明的 machine.agentProfilesPath 之下的每一份文档,加上清单 agents 映射中指名的每一份文档,无论后者位于何处。解析只有单层,因此 rolecore 的配置不得声明 inherits,被指向的目标必须存在、必须id 唯一,且它自身不得声明 inherits。解析的组合方式:

    • 姿态数组requiredReaddefaultContextmustAskWhenmustRefuseWhen):基配置的条目按其撰写顺序在前,然后是派生配置的条目按其撰写顺序,并去掉基配置已经携带的条目。撰写顺序即加载意图,因此不作任何排序。
    • 其余每个字段idnamerolepurposeversionhostinvocationescalationownersfreshness):取派生配置自身的值,绝不继承。inherits 是一条解析指令,本身不属于解析后的配置。
    • 正文:两份正文都是规范性的,基配置在前,派生配置在后。

    无法解析被继承配置的消费者不得单独应用派生文件;派生文件只是一份配置的一半,因此消费者转而报告它不受支持。当某个 ask 条件与某个 refuse 条件同时适用于同一情形时,以拒绝为准。

    解析保证的是组合,而不是语义上的收紧:派生的散文若与基配置矛盾或削弱基配置,即为不符合规范,而且没有任何工具能检测自然语言中的矛盾。

配置调校的是一个角色加载什么、如何行事;它们不复制上下文层的内容。

配置中可选的 hostinvocation 是单一担任者的简写:它们说明如何启用担任该角色的那一个参与者。其 command 是一个模板,遵循与 actor 命令模板相同的规则,包括 <prompt> 占位符及其位置(见 context-layer.md 的“要求”)。当某个角色有多于一个合格参与者,或同一个参与者因担任角色不同而需要不同调用方式时,改由清单中可选的 actors 注册表承载(同一节)。一个角色只用其中一种机制,绝不同时用两种。

注记(非规范性)#

引导配置有意保持简洁:它提供地图与姿态,而不是充当知识库。如果引导配置长达数屏,说明本该归入某个类别的内容被放进了入口。

这套设计旨在避免多余的间接层:智能体最先获得的上下文与真正约束之间每多一跳,就会额外消耗注意力。设计得当的上下文层根本不需要厂商入口文件(调用可以直接指向引导配置),而引导配置直接通向内容。深度应当存在于上下文层的文档中,而不是通往文档的路径上。

引导配置中声明“任何任务之前都要读”的每一份文档,在每项任务上都要付出代价,因此无条件集合是上下文层里最昂贵的空间。把它控制在真正普适的范围内,其余的通过按任务类型的加载、类别、索引,以及每份决策记录自己声明的作用域来路由。索引存在的意义,是让智能体加载任务需要的那一片,而不是整棵树;决策会无止境地累积,因此它们被路由,绝不作为一个目录预先加载。

机器可读接口 ↗拉取请求

机器可读接口

五种产物让工具能够读取上下文层。除此之外,上下文层中的内容都是以人为主要读者、同时供智能体读取的散文;只有这五种产物构成工具所依赖的契约。

产物 默认位置 Schema
清单 leji.json(仓库根目录,固定) context-manifest.schema.json
上下文索引 <root>/context-index.json context-index.schema.json
上下文变更日志 <root>/context-changelog.json context-changelog.schema.json
智能体配置 <root>/agents/*.md(frontmatter) agent-profile.schema.json
决策记录 <root>/decisions/*.md(frontmatter) decision-record.schema.json

除清单外,所有位置都由清单声明;上表给出的是默认值。

要求#

  1. 清单。 leji.json 必须存在于仓库根目录,并通过其 schema 校验。它是 Leji 中唯一固定的文件名:工具可以稳定寻找的那个文件。
  2. 索引。声明 indexed 及以上一致性级别的上下文层必须携带一份上下文索引,且该索引是生成的,绝非手工维护:工具把类别索引文件(categories.<id>.indexes,见 content-categories.md)解析为它们所列的文档,并为每一份受治理的文档写入一条条目。每条条目携带稳定的 id、一个 path、一个 title 和一个 category 标识符。生成器应当同时输出文档的 kindintentrecord);它在 schema 中是可选的,以便在类型概念出现之前写就的索引依然有效,消费者把缺失值视为 intent。当文档声明了合法的 frontmatter date 时,记录的条目还额外携带该日期;生成器只从 frontmatter 取日期,绝不从散文或文件名约定中取。过期的索引(不再与索引文件所解析出的内容一致)必须被视为校验失败。声明了 federation.mounts 的宿主层,还要在同一份索引中携带一个顶层的 mounts 数组:每个挂载一条路由记录(namesourcepin、声明时的 trackingRefowner、声明时的 role,以及路由元数据 categories / topics / requiredWhen)。它们只是路由记录;工具不得把某个同级层的条目或散文复制进宿主索引,而一条挂载记录也不携带任何宿主层受众不该看到的内容(见 distribution.md 的“受限挂载”)。
  3. 变更日志。声明 indexed 及以上一致性级别的上下文层必须携带一份机器可读的上下文层变更日志。条目携带稳定的 id、一个 UTC date、一个 type、一行 summary,以及受影响的 paths。权威顺序是推导出来的,而不是由位置决定的:工具必须(date, id) 升序排列条目,数组位置不承载任何含义。由于 id 在变更日志内唯一(见“标识符”),即便两次变更共享同一个 date(date, id) 仍是全序。留存的条目不可变:只要工具能够确定先前状态,就必须把对已发布条目的修改视为校验失败,而重排数组不算修改。确定先前状态需要一个可供比较的独立基线;当参考工具只有当前版本时(例如普通的持续集成工作副本),这种修改对它不可见,此时是变更集的评审把它抓出来(见 conformance.md)。变更日志用于呈现近期变更,而不是保存完整历史:长期存续的上下文层应当对它做压缩,而不是任由它无限增长;并且可以随时压缩,做法是从该顺序最旧的一端移除条目,前提是同一个变更集追加一条 compaction 类型的条目,其 compacted 字段记录被移除的数量以及首尾两个被移除的 id。移除最旧条目以外的任何内容、没有 compaction 条目的移除,以及压缩到空文件,都是校验失败。仅可追加的纪律id 为键在集合意义上成立,并对照先前提交的状态检查,因此它在撰写时需要 git;文件本身对消费者而言与 git 无关,完整记录由 git 历史保存。触及受治理文档(类别索引文件解析出的那些)的变更集必须追加一条条目,其 paths 覆盖它改动过的受治理路径,使每一份被改动的受治理文档都落在某条被追加的条目之下:仅可追加的纪律保证已发布条目不可变,而这条覆盖规则保证记录完整。可以在旁边另有一份人类可读的变更日志;工具读取的是这份 JSON 记录。
  4. frontmatter 产物。智能体配置与决策记录是 markdown 文档,其 YAML frontmatter 需通过各自的 schema 校验。散文正文保持自由形式;frontmatter 才是机器契约。不得强制要求纯 JSON 的配置或决策:这些文档是给人读的。
  5. 标识符。所有 id 值一经发布必须保持稳定:重命名与移动更新的是 path,绝不是 id。标识符为小写、以连字符分隔,在其产物类型内唯一。生成的索引条目的 id 按优先级顺序推导:文档 frontmatter 中声明的 id(若有);否则是已存储索引中同一路径已经携带的 id,或者对于纯粹保留内容的移动,是同一内容已经携带的 id;否则是文件名的 slug,并与其父目录去重。第一个存在的胜出,因此已发布的 id 能挺过重命名或移动,只有全新的文档才会铸造新的 id。既可能被移动可能在同一个变更集中被编辑的文档应当声明 frontmatter id:只有 frontmatter 能在路径与内容同时变化时锁住 id(按路径承继与按哈希承继两种兜底都会落空),并且当某个已存储的 id 消失时,工具会告警(id-vanished),使它留下的悬空引用能被发现。
  6. 时间戳。变更日志的 date 值是 UTC 的 ISO 8601:要么是日历日期 YYYY-MM-DD(按当天起点 T00:00:00Z 排序),要么是以 Z 结尾的整秒时间戳(例如 2026-06-13T15:04:05Z)。不带时区的时间、非 UTC 偏移与小数秒允许:小数秒会破坏“对 date 做字典序排序即时间顺序排序”这一保证,因为 …05.1Z 明明更晚,却排在 …05Z 之前。所有产物中的每个日期字段都受日历范围约束,因此月份为 13 或日为 99 都是非法的。其他产物的日期遵循 ISO 8601,且可以只到日期。路径为 POSIX 风格,相对仓库根目录,不带前导 ./
  7. 除清单外,每一个 JSON 产物必须声明它所依据撰写的 schema 版本系列(schemaVersion),见 versioning.md;清单则以自命名的 leji 键声明它面向的规范版本系列。
  8. 派生产物继承访问约束。索引、变更日志、生成的查看器,以及任何由上下文层内容构建的编译或导出视图,都是派生产物,智能体基于这些内容产出的输出同样如此。派生产物承载它所取材的内容中最受限那部分的访问约束。不得在没有明确的、经过评审的脱敏步骤(该步骤产出面向该受众的独立产物)的情况下,把派生产物写入或复制到受众比该内容更广的位置;智能体也不得把受限上下文引用或摘要进受众更广或访问限制更少的内容载体(拉取请求、工单、聊天、提交信息或公开的上下文层)。一个受限上下文层的索引,可能与它的散文一样敏感:标题、路径与摘要都在描述它。这是对操作工具的人与智能体的约束,而不是工具执行的检查:Leji 没有定义任何工具可读、可据以计算“更广受众”的受众模型(访问权属于版本控制系统,见 governance.md 的“访问边界”),因此参考 SDK 不强制执行它,工具至多只能告警(查看器导出会提示要私有部署)。

任务路由#

索引、类别归属与决策记录之所以存在,是为了让智能体加载一项任务所需的那一片上下文,而不是整棵树。本节以规范性的方式定义任务作用域如何选出那一片。它是本规范其余部分所引用的唯一路由算法:引导配置的“加载”一节(boot-profile.md)用任务语言把智能体指向这里,决策记录的作用域(decisions.md)由它来匹配,联邦读取(distribution.md)也复用它来判定任务触及哪些同级层。路由读取的是上下文;它不是任务信封,也不是执行协议,那些都留在 1.0 之外(见 README.md 的“扩展边界”)。

  1. 输入。一项任务的作用域,是它读取或改动的、相对仓库根目录的 POSIX 路径集合(按要求 6 归一化:POSIX 风格、相对根目录、不带前导 ./),加上任务显式点名的类别,以及任务显式点名的主题。主题是显式输入:算法绝不从路径、类别、散文或内容中推导它们。智能体或工具如何从任务推导出作用域不在规范范围内;下面的匹配则在范围内。
  2. 路径匹配(字面、双向)。一个声明的路径与一个任务路径匹配,当且仅当归一化之后(POSIX 风格、相对根目录、不带前导 ./、去掉任何尾随 /)两个字符串相等,或者其一是另一个的路径前缀祖先:较短者等于较长者在某个 / 边界处截断后的结果。匹配是纯字面的:它绝不查询文件系统,也不区分某个路径指向的是文件还是目录,因为归一化之后二者无从分辨。这是双向的包含关系(各参考实现共用的 underPath 关系),因此作用域宽泛的任务与声明狭窄的选择器,无论哪一边更宽都能互相找到。
  3. 类别匹配(狭义),以及两个类别集合。一项任务的类别分为展开示意两类。任务显式点名的类别同时进入两个集合。若某个任务路径本身就是一份受治理的文档(按完全相等匹配到它生成的索引条目,绝不按包含关系),则它把该条目的类别只贡献给示意集合。展开类别会加载它的意图文档与记录候选;示意类别只是决策与联邦挂载的匹配信号,自身不加载任何东西。类别选择器不得为仓库中的任意文件推断类别,而本身不是受治理文档的任务路径,包括受治理文档的任何祖先目录,都不贡献任何类别。路径作用域触及文件;类别展开不随之延伸。
  4. 主题匹配(精确、仅限挂载)。主题是由 Unicode 标量值构成的非空字符串,按其 UTF-8 编码比较;孤立代理项不是合法主题。两边都受这条规则约束:任务主题或挂载 topics 中不是由 Unicode 标量值构成的非空字符串的条目属于输入错误,实现必须拒绝它,而不是当作静默的不匹配返回。任务主题与声明主题匹配,当且仅当两个解码后的字符串完全相等。实现不得对任一边做大小写转换、Unicode 归一化、按区域设置比较、裁剪、分词、子串匹配或模糊匹配,因此规范等价但字节不同的写法不会匹配;这条相等规则与下文结果的按字节排序是两回事。重复的任务主题构成一个信号,因此点名两次与点名一次的匹配结果完全相同。当任何任务主题等于某个挂载所声明的任何主题时,该联邦挂载匹配。主题匹配只选中挂载本身:它不得进入展开或示意类别集合、加载任何文档或记录、路由任何决策、求值 requiredWhen,也不得使某个挂载成为必读。
  5. 状态过滤。只有 status 具有约束力的决策记录才作为当前指引被路由。accepteddeprecated 具有约束力;deprecated 的记录带着过期的姿态生效,智能体必须把它当作正在退场的指引,而不是已成定论的当前实践。superseded 的记录不得具有约束力,只能作为历史,并必须携带 supersededByproposedrejected 的记录不得具有约束力。具有约束力的记录称为live
  6. 无作用域的决策。既未声明 affectedPaths 也未声明 affectedCategories 的 live 决策记录是组织级的:无论任务作用域为何,它对每一项任务都被路由。带作用域的 live 决策,只有在任务按路径(第 2 条)或按类别(第 3 条)与之匹配时才被路由。
  7. 空路径作用域,以及空作用域。当任务的路径集合为空时,路径匹配不贡献任何东西,智能体必须声明未曾求值按路径的路由;显式点名的类别仍然生效并仍会展开,显式点名的主题仍会被匹配。点名的类别与点名的主题都算作非空作用域。只有当任务既未点名路径、也未点名类别与主题时,它的整个作用域才为空;此时智能体只路由无条件的引导配置与智能体配置上下文,加上组织级的无作用域 live 决策。智能体不得把一次未经路由的加载呈现为经过作用域筛选的结果。
  8. 记录作为候选被路由。展开类别把它的意图文档作为必需上下文路由;该类别的记录则单独返回,各自带上类型与日期,作为读者凭判断加载的候选。记录只有在以下情形才成为必需:任务的路径按第 2 条直接选中它;智能体或引导配置点名它;或者有人要求它。仅仅因为是类别匹配,或者因为日期最新,绝不使一条记录成为必需。当某条记录既是类别候选、又被路径直接选中时,直接选中胜出,它是必需的。路由不得把任何记录认定为“最新的”或“当前的”:1.0 没有定义记录系列的身份或顺序保证,因此新近度的判断属于读者,依据索引呈现的日期来做。决策记录保有自己的路由方式(第 5、6 条),绝不作为普通记录被路由。把某个决策文件作为任务路径点名,并不会路由那个决策;决定它的是它声明的作用域。
  9. 引用。加载了被路由决策记录的智能体必须说明自己加载了哪些匹配到的记录,使读者能看到智能体应用了哪些指引,并据此推断它没有应用哪些。

被路由的那一片就是智能体为一项任务所加载的内容。它是以下各项的并集:引导配置的无条件加载集合与当前生效智能体配置的 requiredRead(智能体把它们作为与任何作用域无关的基线持有);每个展开类别中的每一份受治理意图文档;任务路径按第 2 条选中的每一条受治理条目;按第 8 条被路径直接选中的每一条记录;以及任务按路径、或按示意或展开类别匹配到的每一条 live 决策,再加上组织级的无作用域 live 决策。

示意类别只贡献匹配,作用于决策与联邦挂载,绝不展开任何语料。

路由结果不是完整信封。计算路由的工具在返回所选内容的同时,还会返回智能体不得在无人提示时加载的材料:记录候选及其候选路由元数据。如果不加区分地加载整个信封,路由也就失去了意义。

结果顺序具有规范性(当工具输出顺序时),以便各独立实现逐字节一致:类别按本规范的权威类别顺序;文档、记录与决策按路径升序;挂载按名称升序。字符串比较是按 UTF-8 字节进行的,不依赖区域设置或码点排序规则。

工具可以提供一个由路径集合计算这一片的辅助函数;各参考实现都提供了一个(route)。这类辅助函数计算的是依赖作用域的那部分,并不要求输出基线,因为调用方本就持有基线;智能体加载该基线的义务不变。只要一个原始读取者照此算法执行,路由即为符合规范。

注记(非规范性)#

参考工具目前会检查变更日志的 schema 与仅可追加纪律(leji validate 两者都跑);对照某个基线版本验证变更日志的覆盖度,即每一条被改动的受治理路径都出现在某条被追加的条目中,是路线图上的一项报告式检查,尚未成为阻断性关卡。在它发布之前,覆盖度靠流程担保的评审与 CI 纪律来保障(见 conformance.md)。

索引是受治理上下文的导航来源:leji viewer 根据索引呈现受治理的主干,并在下方将仓库自身的目录树显示为可浏览的参考区,因此同一个视图既包含受治理的上下文,也保留团队原有的导航。其他文档工具也可以用相同方式投影索引。具体呈现方式不属于规范范围。这个接口有意保持精简:五种形态足以让工具校验上下文层、比较差异、评估时效性,并将智能体路由到正确内容;数量又足够少,团队可以记住整个接口。超出这五种形态的内容属于 1.0 之后的范围,将由实践中的真实需求决定。

决策 ↗拉取请求

决策

决策记录是上下文层中带日期的“为什么”:架构决策、厂商选型、范围边界,以及有意作出的“不做决定”。它们可以避免同一问题被反复讨论,也让智能体获得决策背后的推理,而不只是规则本身。

决策记录是记录的正式子类型(见 content-categories.md 的“意图与记录”):它们本质上就是记录,并拥有普通记录所没有的统一 schema 与生命周期。它们生成的索引条目携带 kind: record;决策记录自身不声明 kind 键(其 schema 是封闭的,显式写 kind 会导致校验失败)。

要求#

  1. 决策记录是带 YAML frontmatter 的 markdown,frontmatter 对 decision-record.schema.json 有效,一份记录一个文件。决策语料由清单声明的两个收录来源取并集得到,上下文层可以使用其中之一或两者:所声明的记录路径(machine.decisionRecordsPath,默认 <root>/decisions/),以及 decisions 类别的索引文件所解析出的条目。一份记录必须至少能通过其中一条途径到达。
  2. frontmatter 必须携带:id(稳定)、titlestatusdatestatusproposedacceptedsupersededdeprecatedrejected 之一。
  3. 正文必须用散文写明:背景(是什么局面迫使做出决策)、决策本身,以及它的后果。推荐的章节标题是 ## Context## Decision## Consequences;记录可以再加一节 ## Alternatives
  4. 记录是仅可追加的历史不得把一份记录改写成另一个决策。随着决策变老,frontmatter 中有两个字段是可变的status(它的生命周期)与 supersededBy(在它被取代时设置);其余一切,id、原始的 titledate、所声明的作用域,以及散文正文,一经发布即不可变。翻案或变更是一份新记录,其 frontmatter 设置 supersedes,而旧记录的 status 变为 superseded 并设置 supersededBy。取代关系的链接必须在两个方向上保持一致:当记录 B 设置 supersedes: A 时,记录 A 携带 status: supersededsupersededBy: B,而 superseded 的记录必须supersededBy 中指名它的后继者。两份记录都保留。参考工具目前会强制执行取代关系的双向一致性。它尚未验证不可变性本身(即已发布记录被冻结的字段与正文相对某个基线版本未被改动);那是路线图上的一项报告式检查,尚未成为阻断性关卡。在它发布之前,不可变性靠流程担保的评审纪律来保障(见 conformance.md)。
  5. 记录可以声明 affectedPathsaffectedCategories,使工具能够从一项任务的作用域路由到治理它的那些决策。任务作用域如何选中记录(考虑重叠的路径包含、狭义的类别匹配、accepted / deprecated 的约束力,以及对两者都不声明的记录按组织级处理),见 machine-readable-surface.md 中的“任务路由”算法。
  6. 被否决的提案同样属于记录(status: rejected)。将未被采纳的决策记录下来,是成本最低的防止重复讨论的方式。

与 ADR 的兼容(非规范性)#

Leji 的决策记录刻意与架构决策记录(ADR)兼容:既有的 ADR 目录只需为每份记录(或此后的新记录)添加那些 frontmatter 字段,并在清单中映射该目录,即可满足 decisions。不需要任何 ADR 工具,也不排斥任何 ADR 工具。

治理 ↗拉取请求

治理

治理是上下文层区别于 wiki 的关键。其核心语义就是圆环:平等的是访问权,而不是决定权。

圆环的规范表述#

  1. 所有人都读取。所有参与者,无论是人还是智能体,只要能访问某个上下文层,就必须能够读取它的全部内容。一个在其内部按角色限制阅读的上下文层不是共享上下文层;当不同的人只能读到不同的材料时,那些材料就应当分属不同的上下文层(见下文的访问边界以及 distribution.md)。
  2. 任何参与者都可以提议。任何参与者,无论是人还是智能体,都可以提议对上下文层的变更。智能体撰写的提议是一等的:智能体在工作中发现上下文缺失或有误时,应当在引出该问题的同一个变更集中提议修复。提议应当携带足以让评审者理解其意图与预期效果的理据;那份理据是一个人做出批准所需的最低限度。Leji 1.0 没有定义通用化的证据协议(见 1.0 的范围)。
  3. 由人来批准。对上下文层的每一次变更,在成为权威内容之前必须经过一个人的批准。批准搭载仓库既有的评审机制(拉取请求);Leji 不引入单独的流程。参与可以通过任何界面发生,但权威的批准必须是该机制中一条可审计的评审记录:可归属到批准人,并绑定在被评审的变更集上。仅存在于外部讨论、聊天、工单状态或文档评论中的批准,在它成为这样一条记录之前不算数;把它复述进一条评论也不算。拓宽人们的参与方式,绝不改变批准权记录在哪里。

要求#

  1. 归属,而非署名。清单必须指名一位主负责人(owners.primary),并可以指名一位延续负责人(owners.continuity):另一个人,在主负责人不在或离开时承担同样的责任。有外部协助的接入应当在外部协助撤离之前指名延续负责人;单人维护的上下文层可以没有,这诚实地表明它没有继任安排。负责人是承担责任的:智能体可以提议与评审,但绝不承担归属,而把主负责人再写一遍充作延续负责人等于没写。负责人对上下文层的健康度负责:它保持最新,过期或自相矛盾的内容被裁剪,每个领域都有人照看。负责人不是内容策展人。内容由整个圆环在工作过程中书写并保持真实;把这件事集中到一个看守者身上,正是这套模型要避免的瓶颈。
  2. 评审范围。上下文层的变更应当由最贴近受影响内容的人来评审,也就是领域负责人,而不是汇集到单一把关人手中。领域归属沿用仓库既有的归属映射(CODEOWNERS 文件、团队约定),不是清单里新增的字段;Leji 复用它,正如它复用拉取请求来完成批准。主负责人负责确保每个领域都有人。评审要问的不止“这是不是真的”:为什么它属于上下文层、谁会依赖它、什么证明它成立、以及什么时候该重新审视它。回答不了这些的变更是一条链接或一则笔记,不是权威上下文。任何变更集都要面对的常设问题是这次变更是否改变了上下文?;如果是,上下文的增量就应当在同一个变更集里。
  3. 纳入与移除。提议是开放的;纳入不是。内容只有在会改变未来工作的做法时才属于上下文层:它设定约束、编码决策、定义接口或归属边界,或者终止某个反复出现的错误。其余的只做链接,不做吸收。上下文层必须拥有一条与其批准路径同样审慎的移除路径:过期的、被取代的与重复的内容,在日常的、经过评审的变更集中被裁剪,而裁剪是每位领域负责人的职责,不是一个单独的清理项目。一个只增不减的上下文层,会一边通过评审一边腐烂。持久性的指引应当通过“两次验证”门槛(见 content-categories.md):一次性的修复可以合并,但一条规范只有在至少两项真实任务中都成立之后才成为权威。沉淀真实成立的东西,而不是期望成立的东西。
  4. 变更日志纪律。indexed 及以上一致性级别,每一次被批准的上下文层变更必须machine-readable-surface.md 追加一条机器可读的变更日志条目。
  5. 时效性。时效性是意图的机制:意图文档与智能体配置应当携带复核期限(索引条目与配置中的 freshness.reviewAfter),而记录不携带(它的日期本身就是它的时效,见 content-categories.md;在记录上声明期限是校验错误)。工具应当报告期限已过的意图内容,并不得把过期内容悄悄当作当前有效。为某项任务加载上下文的读取者,必须在该任务的输出中呈现任何复核期限已过的已加载条目,使陈旧对人可见,而不是被埋没。运营序列中下一份应有的记录是否逾期,是另一个概念(流式新近度);1.0 只是点出它,并未为它定义机制。一项任务的必需上下文,是引导配置无条件加载集合、当前生效智能体配置的 requiredRead,以及任务路由算法为该任务选出的那一片(它路由到的 live 决策与受治理意图文档,加上任务路径直接选中的任何记录,见 machine-readable-surface.md)三者的并集。当某个必需条目的期限已过期时,读取者必须停下或发问,而不是照此推进;明知上下文已过复核期仍据以行动,正是这条规则要防止的“无声陈旧”失败。并非必需的过期条目可以在标注其陈旧状态后使用。在 governed 一致性级别,复核期限必须被声明并被检查(按一致性检查清单,仅报告式的检查即可接受);推荐在 CI 中运行该检查。复核时效(上文)与工作副本时效是两回事:后者指读取者手上的副本是否与权威仓库一致。读取者从版本控制系统(git)确定工作副本时效;工作树只对所检出的那个版本是当前的,工具不得把未经验证的副本悄悄当作当前有效。以普通文件内容接触上下文层、且没有可用的 git 工作树或版本元数据的读取者(文件内容被上传或同步到另一个界面,而仓库并未随行),必须把工作副本时效视为未知,而不是当前有效。
  6. 权威内容存放在上下文层里。治理工作方式的知识不得只存在于某个厂商配置文件、某段聊天记录或某个人的笔记中。若它治理工作,它就属于上下文层,并接受评审。

访问边界#

Leji 自身没有定义任何访问控制机制。对上下文层的访问由版本控制系统(git)与仓库所在的平台治理:仓库托管平台的权限,以及暴露工作树的文件系统或共享盘。访问的单位就是上下文层。

  1. 上下文层可以存放在受访问控制的仓库中。Leji 不授予、不检查、也不强制执行那种访问权;版本控制系统与它的托管平台才做这件事。
  2. 符合规范的上下文层不得在其内部要求按角色限制阅读。“所有人都读取”的范围是一个上下文层的受众:凡版本控制系统准许进入的人,都读取该上下文层的全部内容。
  3. 需要更窄受众的内容(高管、财务、安全或事故响应类的上下文)必须存放在一个单独的上下文层里,拥有自己的仓库、清单、负责人与评审关卡,并由版本控制系统授权。受限的上下文是一个单独的上下文层,绝不是共享上下文层中的一块受限区域。
  4. 把一个受限层组合进另一个团队的上下文,属于联邦的情形,并适用 distribution.md 中针对受限挂载的附加规则。

维护模型(非规范性)#

上下文层以小步增量的方式维护,并融入原本就在进行的工作:任务暴露出缺失或错误的上下文,修复随同一个经评审的变更集提交,再由变更日志记录。无需专门安排文档冲刺。错误的上下文会产生错误结果,而且通常当天就会被察觉;这种反馈与评审、CI 自动检查共同构成完整的强制机制。

快速反馈可以及时发现错误的内容。至于缓慢积累的平庸或冗余内容,则由各领域负责人依照上文的纳入门槛和移除路径处理。这属于内容策展,而 Leji 有意将其分散。由单一策展人把关看似稳妥,实际上会让其成为系统中最慢的环节:变更要么排队等待,要么绕过把关人,而单一策展人掌握的上下文通常也不及各领域专家。最终,上下文层不是停滞,就是分裂。由每位领域负责人分别裁剪和把关自己负责的部分,才能让整个上下文层保持精简、真实,并避免形成瓶颈。负责人维护系统的健康度,圆环中的参与者维护内容。

分发 ↗拉取请求

分发

本章说明上下文层相对于其所描述的工作应当存放在哪里。共有三种模式,但始终遵循同一条规则:上下文层只包含文档,不得为任何消费它的仓库引入构建或运行时依赖。

模式 1:单体仓库(默认)#

上下文层与它所描述的代码和基础设施位于同一个仓库,存放在上下文根目录下。凡是团队的工作都在一个仓库里的情形,这都是推荐的模式:代码、基础设施与上下文一起纳入版本管理,漂移在结构上就很难发生。

模式 2:面向多仓库场景的“只含文档”子模块#

当工作跨越多个仓库时,上下文层存放在一个专用的上下文仓库中,各消费仓库以 git 子模块的方式挂载它。

  1. 上下文仓库是一个普通的 git 仓库,有自己的 leji.json、分支策略与评审关卡。
  2. 消费仓库必须把它挂载在固定路径上(推荐context/),并不得让任何构建或运行时步骤依赖它的存在:缺失或过期的挂载削弱的是知识,绝不是构建。
  3. 每个消费仓库固定上下文层的某个具体版本。固定版本的更新必须以可评审的变更集形式到来(脚本化或机器人提出的拉取请求),使上下文变更在每个仓库中都可见、可评审、可归属。
  4. 工具应当报告过期的固定版本(每个消费仓库落后上下文层多少)。过期固定版本的报告必须先于任何阻断性强制:先可见,后设关卡。1.0 参考 SDK 的固定版本报告覆盖的是联邦挂载(模式 3);对本模式中消费侧的固定版本,它没有提供检查,因此在 federated 级别这一条由流程担保(见 conformance.md);在参考检查落地之前,由团队或团队自己的工具来报告它。

模式 3:同级上下文层的联邦#

模式 1 与模式 2 各自只有一个上下文层:单体仓库拥有一个,多仓库组织消费一个。联邦面向的是这样一种组织:其中已经有不止一个团队各自拥有自己的上下文层,目标是让这些上下文层彼此可读,而无需任何人交出控制权。

人们往往首先想到把它们合并:建立一个上下文仓库,将各团队的知识集中起来。但应当避免这样做。上下文层能够保持最新,是因为负责人会在每项任务中读取它,并在同一个变更集中修正错误。将产品团队的上下文移入平台团队的仓库,会让产品内容与相应责任分离;当所有人都以为“现在归别人维护”时,内容就会逐渐失效。集中知识,只会重新制造那个曾将知识困在人脑和聊天记录中的瓶颈。

联邦是组合这些上下文层,而不是吸收它们。一个团队的上下文层作为同级层加入另一个团队的图谱:已挂载、被引用、被读取,绝不被复制。

  1. 同级上下文层以宿主清单 federation.mounts 中声明的固定版本挂载加入:同级层的 nameowner、它的 source 仓库定位符,以及一个 pin,指明宿主所读取的同级层版本的完整不可变提交 id。这个固定版本是该挂载的版本记录,保存在清单自身中,因此固定版本的更新以可评审的变更集形式到来:挂载记录的是本仓库当时读取的是另一个团队真实内容的哪个版本,而不是它的一个分叉。挂载可以声明 trackingRef,即 source 上一个全限定的分支或标签,过期与可达性都相对它来判定;未声明时,检查时使用 source 所公布的默认分支,并在报告中写明。

  2. 同级层保留一切让它保持活着的东西:自己的仓库、负责人、评审关卡、变更日志与一致性声明。宿主层不得把同级层的内容复制到自身。与拥有它的团队分离开的内容会在无人负责的情况下变得过期,而这正是联邦要防止的失败。

  3. 已挂载的内容物化为一份由解析器填充的层投影,绝不是被提交的副本。同级层通常是嵌在一个更大仓库中的层(模式 1),因此整体检出该同级层就等于为了读它的上下文而把一个产品也一并纳入。取而代之,工具在固定版本处抽取层投影,即同级层自身清单所使其可读的一切内容去重后的并集(根部的 leji.json、所声明上下文根目录下的整棵树、引导配置、在该固定版本处存在时的机器索引与变更日志文件、存在时的智能体配置树与决策记录树、agents 绑定所指名的每一份智能体配置、每一个类别索引文件,以及被固定版本处生成的上下文索引所列出的每一条受治理路径,无论它们位于何处;同级层自己的清单定义它的投影,宿主层绝不参与挑选),放入一个宿主层版本控制会忽略的临时缓存中。失败边界也沿同一条线:在该固定版本处缺失的被引用或 schema 要求的文件(引导配置、某个类别索引、某份被绑定的智能体配置、某条被索引的受治理路径)会使投影失败,并给出一个稳定的错误码,写明是哪份产物声明了它以及缺失的路径;而缺失的目录或缺失的机器产物既不贡献任何内容也不导致失败,无论其实际位置是声明的还是默认的;git 无法表示空目录,而没有生成索引的层,除了根部的树之外本就没有内容闭包。可用性一类的投影失败(固定版本处的内容缺失或格式错误)会使该挂载在这台机器上不可用,但绝不会使宿主层的普通校验或宿主的产品构建失败。安全性或内部性的投影失败(逃逸的路径、格式错误的字符串、超出的上限)会以非零退出码中止填充,且绝不会发布任何部分投影。固定版本对应的字节从 git 对象库解析而来(机器本地的提示仓库、解析器管理的对象库,或宿主层某个子模块的对象数据库),绝不从任何工作树读取,而网络访问只作为一个明确的、经过同意的步骤发生。宿主仓库可以出于自身原因携带同级层的子模块;工具只把它当作又一个本地对象库,而一份工作副本绝不是可读的挂载内容。把同级层的内容提交进宿主层,包括缓存内容,都不符合规范。模式 2 的“只含文档”规则在此天然成立:宿主层中没有任何东西针对该投影构建或运行。

    解析到仓库根目录的 machine 路径什么也不选中。若所声明的 machine.agentProfilesPathmachine.decisionRecordsPath 解析到同级层的仓库根目录,它不会为投影贡献任何目录选择:照办就等于把整个同级层仓库纳入,而层投影存在的意义正是避免这个结果。被引用的内容不会丢失,因为通过 agents 绑定或固定版本处生成的索引被逐一指名的配置与决策记录仍会随投影而来;被丢弃的只是那种笼统的根目录选择。

    投影上限。解析器必须强制执行四条上限,使独立实现拒绝的是同一批输入,而不是各自选定自己的天花板。一个投影最多携带 65,536 条条目,按去重之后计数。它的内容总量最多 2 GiB(2,147,483,648 字节)。任何单条被投影的路径都不超过 4,096 字节,按该路径的 UTF-8 编码度量,而不是按字符、码点或任何运行时的原生字符串单位,因为后者各实现之间并不一致,否则会接受不同的路径集合。一次整棵树的列举在传输中最多占用 256 MiB(268,435,456 字节);这限定的是解析器为了做选择而读取的枚举元数据,而不是由字节上限约束的被投影内容,两者刻意取不同的数值,因为一个庞大的仓库完全可能只对应一个很小的合法投影。四者中任何一条被突破,都属于安全性一类的投影失败。

  4. 未被物化的挂载削弱的是知识,绝不是构建。校验区分三件事。撒谎的清单是错误:重复的挂载名、某个挂载复用了宿主层自己的 name,或者 sourcepin 缺失或格式错误。已声明但在这台机器上根本没有填充的挂载是告警:诚实的降级可用性,被报告并跳过。已物化投影相对其固定版本的完整性,是工具呈现的诊断信息,只有在选择开启强制时才是致命的。普通校验不得因为某个挂载不可用而失败、发起抓取或提示;想要强制的宿主层要显式选择开启(联邦健康检查可以先填充再要求可用性),而读取者面对任务必需却不可用的挂载时的义务,是下文的“失败即关闭”规则。

  5. 挂载启用的是读取,不是决定权,也不授予访问权。挂载了某个同级层的宿主层,会在读者与智能体本就有权访问它时把他们路由进去;挂载既不授予那份访问权,也不批准同级层的变更。每个上下文层的写入仍由它自己的负责人批准,谁可以读它仍由版本控制系统决定。联邦为相关仓库本就准许的参与者组合出可读的上下文;它让“谁来批准”和“谁可以读”原封不动。

  6. 挂载是直接且扁平的。宿主层组合它所指名的同级层;工具不得递归进入同级层自己的挂载,传递而来的上下文只作展示:同级层所声明的挂载,若在宿主层没有直接的固定版本,就绝不会被解析、索引或路由。每个挂载的 name 在宿主清单内必须唯一,并不得复用宿主上下文层自己的 name。由于任何东西都不会越过一个上下文层所声明的同级层继续遍历,菱形结构与环都是惰性的:A 挂载 BC,而 B 也挂载 C,这只是三段直接关系,不是一张需要遍历的图。

已挂载的上下文层是彼此独立的具名来源,不会并入宿主层的类别。宿主层自己的上下文层对宿主仓库具有权威性;每个同级层对它自己具有权威性。不存在组织级的命名空间,因此也没有跨同级层的优先级需要裁决:智能体从拥有它的那个上下文层加载所需的那一片,并写明来源。而且已挂载的内容是不可信输入:它是可读的上下文,绝不是可执行的指令。同级层的散文与其他可读输入来源一样,可能包含错误或被注入的指令,因此智能体把它当作需要权衡与引用的材料,对自己的行为套用宿主层自己的姿态,绝不把挂载中出现的祈使句当作宿主层的指令来服从。

过期固定版本的报告是知晓祖先关系的,并且对自己能看到什么保持诚实:工具把固定版本与见证 ref(trackingRef,或 source 所公布的默认分支)比较,报告为最新、落后 N 个、领先、已分叉或无关联,并且始终写明所比较的 ref、比较所在仓库的类别(解析器管理的对象库、机器本地的提示仓库,或宿主层的子模块)、见证 ref 是解析器自己的还是它并不拥有的、观察时间,以及祖先信息是否完整。当没有任何对象库可达时,报告为 unknown,绝不猜测。只能通过机器本地提示解析到的固定版本,确立的是可用性而非一致性:在 federated 级别,固定版本必须能从 source 所公布的某个 ref 到达(见 conformance.md),而无法到达 source 的检查报告 unknown,这绝不授予该级别。

一个上下文层只有在这些关系真实存在且可检查时才达到 federated 一致性:该上下文层被至少一个其他仓库作为固定版本挂载消费,过期固定版本的报告已经就位,并且每个已声明的挂载都携带完整的固定版本声明(source、完整提交固定版本、路由元数据)且归属完好(见 conformance.md)。参考 SDK 检查其中机械的部分并报告问题;物化状态刻意不作为一致性的输入,因为某一台机器上的可用性,对声明是否属实什么也说明不了。

这一形态的成型清单示例见 examples/multi-repo/

圆环组合归属,而不是把归属集中起来。单体仓库是一个团队的人与智能体围绕一个上下文层构成的圆环;多仓库组织则是这些圆环构成的圆环,每一个仍由让它保持真实的那些人拥有。

读取一个联邦化的上下文层#

发现同级层是宿主层要让它清晰可见的职责,不是智能体要去推断的事。声明了挂载的宿主层,会在智能体本就会读的两个地方呈现它们:引导配置用任务语言指名它的同级层(见 boot-profile.md),生成的上下文索引携带一个 mounts 路由数组(见 machine-readable-surface.md)。智能体永远不必为了找到同级层而去读清单。

读取一个联邦化的宿主层时,智能体:

  1. 先加载宿主层的引导配置与宿主层的机器可读接口;宿主层自己的上下文层对宿主仓库具有权威性。
  2. 在确定任务的上下文作用域之前,先读取宿主层可见的挂载路由记录。当宿主层引导配置、某条索引挂载记录,或该挂载的 requiredWhen 元数据表明任务需要它时,该挂载是任务必需的;当在任务路由算法(machine-readable-surface.md)之下,它的 categories 至少有一项匹配某个示意任务类别,或它的 topics 至少有一项与任务显式点名的某个主题完全相等时,该挂载是任务相关的。主题匹配只选中挂载本身:它不展开任何类别,也不在同级层内部选中任何内容。随后由同一个算法,从同级层自己的索引中路由出智能体要加载的那一片。
  3. 要加载一个任务相关的同级层,从解析器状态获取已填充投影的位置(参考 SDK 的 mounts locate;绝不通过推断缓存路径),在那里读取同级层的 leji.json,核对同级层的 name 与宿主层的声明一致,读取同级层的引导配置,然后只从同级层自己的索引加载任务需要的那一片。事实、约束与引用都带上它们所来自的上下文层名称,而已挂载的内容按本模式的规则始终是不可信输入。
  4. 不得递归进入同级层自己的 federation.mounts。如果某个孙代上下文层对宿主层的任务确实必要,宿主层必须把它声明为自己的直接挂载。
  5. 按归属套用姿态:宿主层的姿态治理在宿主仓库中的工作,而同级层的姿态治理对该同级层内容的解读与对它提出的变更。当宿主层与同级层的指引在同一项任务上冲突、且找不到唯一具有归属的上下文层时,智能体必须停下并发问,而不是自行选择一个从未言明的优先级。

工具可以提供理解挂载的加载辅助能力,但读取同级层并不需要 Leji 的工具:只要遵循这个流程并保持访问边界,原始的仓库读取同样符合规范。

受限挂载#

当被组合的各层受众不同时,联邦就跨越了一条访问边界(见 governance.md)。访问权仍由版本控制系统来强制:读取者要么能解析某个挂载的仓库,要么不能。规范的职责,是让那条边界既不泄漏也不无声地失效。

  1. 受限层不得被声明为受众比它自身更广的宿主层中的挂载:宿主层准许的每一个参与者,都必须本就已被那个已挂载的层准许。挂载声明本身(它的存在,以及 nameownerrolecategoriestopicsrequiredWhensourcepintrackingRef不得披露任何宿主层受众不该看到的内容。当更广的受众需要某个受限决策时,发布一个脱敏的伴随层或一份公开的决策摘要,而不是一个指向受限层的挂载。

  2. 挂载是一次引用,不是一次授权。声明挂载绝不会把“谁可以读已挂载的那一层”扩大到版本控制系统已经允许的范围之外;某个读取者能否解析它,由那里决定,不由宿主清单决定。

  3. 失败即关闭,绝不无声。无法解析某个任务必需挂载(定义见读取一个联邦化的上下文层)的读取者必须停下并报告上下文不完整。它不得当作那个不可访问的层不存在一样继续推进:智能体基于自己看不见的残缺上下文行动,正是这条规则要防止的失败。反过来同样是失败:能够解析某个任务必需或任务相关的同级层却仍然跳过它的读取者,是在基于无声残缺的上下文行动,不符合规范。

    工具在这里能担保什么、不能担保什么:校验器报告本地可用性(某个已声明挂载在这里没有已填充的投影),而路由算法按类别与主题的重叠判定任务相关性(各参考 SDK 会呈现任务相关的挂载;见 machine-readable-surface.md 的“任务路由”)。但任务必需性取决于 requiredWhen,那是自由文本的任务条件,而运行时可达性取决于读取者在读取时自身的访问权;两者都由智能体判断,不由工具判断。因此那条“失败即关闭”的 MUST 由智能体担保:工具呈现它能看到的,智能体执行那个停下的动作。

注记(非规范性)#

子模块的负面印象,主要来自与构建流程耦合的代码子模块。只含文档的叶子节点不存在这些故障模式:没有内容需要针对它编译,即使版本落后也不会破坏构建。固定版本只是记录“本仓库当时依据真实内容的哪个版本工作”,提供的是信息,而不是风险。

联邦看似比合并多出更多活动部件,实际反而更少。合并的初始成本很低,长期成本却很高:此后的每次跨团队编辑都要经过中心仓库的负责人,而没有团队日常读取的部分,恰恰最容易失效。同级挂载让每个上下文层保持精简、归属明确并持续有人读取,代价只是更新一次固定版本;这只是一份可评审的 diff,而不是一场协调会议。

一致性 ↗拉取请求

一致性

规范有意允许部分接入。四个级别逐级累加,每一级都包含前一级;团队在清单中声明自己的级别(conformance.claimedLevel)。一致性完全由团队自我声明,不设认证计划。

一致性是针对检查执行的位置上实际物化出来的那个上下文层来评估的,而不是针对某份副本可能代表的那个权威层。脱离仓库拿到的副本,按 context-layer.md 的降级模式读取,而降级读取绝不是通往权威性的路径:这样的副本无法通过验证,工具会明确这么说,而不是把问题悬着。

检查清单中的多数条目是机器验证的:参考工具对照该层检查它们,并在不成立时判定声明失败。报告有四种结果,它们刻意不可互换:

  • fail:证据已经取得,而该要求未被满足。
  • (流程担保),报告为 manual:该条目描述的是团队实践(一道评审关卡、一个 CI 任务、一个外部消费方),任何工具都无法仅凭仓库确认,因此由团队为它背书。只有下文标注了**(流程担保)**的条目才会以这种方式报告。
  • unknown:一个机器条目,但本次运行无法取得其证据,例如没有 source 访问权时的联邦固定版本可达性检查,或者没有 git 基线可比时的仅可追加纪律。unknown 绝不授予级别,也绝不推翻一次有证据的运行本可确认的声明。
  • not applicable:一个有条件的机器条目,对本层不适用,例如在未声明任何挂载的层上的联邦挂载条目。它不计分,在任何方向上也都不构成证据。

工具报告的 verifiedLevel,是其适用的机器验证条目全部通过的最高级别,且绝不高于该层所声明的级别failunknown 都会阻止授予,而流程担保或不适用的条目不计分。对声明设上限是刻意的:验证回答的是这个声明是否成立,而不是这个层本可以声明什么,因此一个声明 core 的层即便证据足以支撑到 governed,报告的依然是 core,而提高所报告级别的办法是提高声明。verifiedLevel 绝不为流程担保的条目背书,因此对携带这类条目的级别而言,verifiedLevel 通过是必要条件而非充分条件。除非标注**(流程担保)**,下文每个条目都是机器验证的。

有两个机器验证条目在降级副本中表现不同,其差别源于各自拥有什么证据。git 存在性是有答案的:不在 git 仓库中的副本不满足 core 对“上下文层存放在 git 仓库中”的要求,因此该条目判为 fail变更日志的仅可追加纪律则没有答案:文件本身可能完全格式正确,而用来比较的先前提交状态却不可达,因此该条目为 unknown,该层从那份副本出发就是无法在 indexed 上通过验证。两者都不会报告为 manual,那是为标注了流程担保的条目保留的。另外,时效性的读取者规则(呈现已加载的过期上下文,并在必需条目过期时停下或发问,见 governance.md)是读取者的行为规则,不是一致性关卡:参考实现的 leji route 会为每份被路由的文档标注复核期限与是否过期,供智能体据以执行。

有三个条目今天的验证深度低于它们所陈述的意图,这里把差距点明,而不是留给读者自己去发现。引导配置那一条,验证的是它在所声明路径上的存在性,以及它是否带有身份、加载与姿态这三个标题(每次 validate 都会把缺失的标题以 boot-profile-sections 告警的形式报告出来,不设关卡);而身份那一节是否真的写了实质内容,则由可选开启的 --content 检查负责,这项检查也会指出引导配置里任何位置的占位文本。“真实决策”那一条,验证的是至少一份被解析到的记录具有 schema 合法的 frontmatter;正文是否有实质内容(一个真实的决策,而非空壳)同样依赖 --content。变更日志那一条是第三个:仅可追加纪律是对照文件在 HEAD 处的状态检查的,这能抓住仍在工作树中的改写,也正是 pre-commit 钩子存在的那种情形。在持续集成的工作副本中,工作树就是 HEAD,因此一次已经提交进来的改写对该检查不可见,此时是变更集的评审来兜住它。所以这一条验证的是工作树,不是历史。这三条各自陈述的意图,对于一个符合规范的上下文层应当携带什么,仍然具有规范性;加深机器检查,以及对照一个明确的基线版本比较变更日志,都在参考工具的路线图上。此外,federated 的验证还要求至少有一条已声明的 federation.mounts 条目:只作为提供方的上下文层(被其他仓库消费、但自身不声明任何挂载)验证到 governed,它的联邦地位依托于那些流程担保的被消费条目。

级别 1:core#

上下文层已经存在,人和智能体都能基于它工作。

  • 上下文层存放在一个 git 仓库中,并与它所描述的工作一起纳入版本管理(见 context-layer.md 的“要求”)。
  • 仓库根目录有 leji.json,且对清单 schema 有效。
  • 在所声明的路径上有一份引导配置,涵盖身份、加载与姿态。
  • 至少映射了 domainsystem(经由其索引文件)并已填充至少一份被解析到的意图文档(只有记录不承载任何运作上下文),外加 decisions 且至少有一份真实的决策记录:一份携带具体 status、正文中有真实决策的记录,不是空壳或占位内容。
  • 有一位具名的主负责人。
  • 厂商入口文件若存在,重定向到引导配置。

级别 2:indexed#

上下文层对工具可读。

  • core 的全部内容。
  • 一份生成的上下文索引,与代码树保持一致。
  • 一份机器可读的变更日志;上下文层的变更会追加条目。

级别 3:governed#

强制手段是机制性的,而不是靠善意。

  • indexed 的全部内容。
  • 上下文层的变更搭载仓库的评审关卡;由人来批准。(流程担保)
  • 智能体配置(至少一份 core 配置)对配置 schema 有效。
  • CI 校验这个接口:清单、索引与代码树一致、变更日志纪律、配置 frontmatter、所声明的路径可解析。(流程担保)
  • 复核期限已声明并被检查(仅报告式即可接受)。

级别 4:federated#

上下文层跨越一个多仓库组织。

  • governed 的全部内容。
  • 该上下文层被至少一个其他仓库作为固定版本挂载消费,且固定版本的更新以可评审的变更集形式到来。(流程担保)
  • 过期固定版本的报告已经就位:消费方能看到自己的固定版本落后见证 ref 多少。参考 SDK 那份知晓祖先关系的报告覆盖了已声明的联邦挂载;此外消费侧的报告由团队自理。(流程担保)
  • 任何同级上下文层都按 distribution.md 声明为完整的固定版本挂载:一个归一化的 source 与一个完整的提交 pin,且归属完好。任何单台机器上的物化状态都不是一致性的输入。
  • 每个已声明挂载的固定版本,都能从其 source 所公布的某个 ref 到达(所声明的 trackingRef,或 source 的默认分支)。这项检查需要 source 访问权:没有它则结果为 unknown,而 unknown 绝不授予该级别。只能通过机器本地提示解析到的固定版本,属于可用性,不属于一致性。
  • 每个已声明的挂载都携带路由元数据:至少 categories,加上 topicsrequiredWhen,使智能体无需读取同级层即可判断相关性。
  • 引导配置呈现每一个已挂载的同级层,且生成的索引携带 mounts 路由数组,使智能体无需读取清单即可发现并加载同级层(见 boot-profile.mdmachine-readable-surface.md)。

注记(非规范性)#

core 是上下文层成立的最低要求;indexed 增加了供工具读取的、自动生成的机器可读接口;达到 governed 后,上下文层不再依赖个人自律;federated 则面向这样的组织:其中已有多个团队,各自拥有值得完整保留的上下文层。多数团队达到 governed 即可;federated 专为上述组织设计,并不是成熟度徽章。

版本管理 ↗拉取请求

版本管理

规范、schema 和实现规范的工具分别独立进行版本管理。

规范#

  1. 规范采用 SemVer 版本号(当前为 1.0.0)。破坏性变更需要主版本;每一次变更都记录在仓库的变更日志中。
  2. 上下文层在 leji.json 中通过自命名的 leji 键声明它面向的规范版本系列(例如 "leji": "1.0"),沿用 OpenAPI 的约定。该值是规范的版本系列major.minor),绝不是规范的补丁版本:补丁发布(1.0.01.0.1)打磨措辞或工具而不移动版本系列,因此清单在每一次补丁之后仍保持 "1.0"。工具必须对照所声明的版本系列校验上下文层,而不是最新的版本系列。

预览版本系列#

规范版本系列可以被指定为预览。预览版本系列可以原地修订:在它于正式发布(GA)时被冻结之前,它可以发生本来属于破坏性的变更,而不必提升到新的版本号。“破坏性变更需要主版本”这条规则(第 1 条)与“形态发生不兼容变更时 $id 随之改变”这条规则(第 3 条)自 GA 冻结起生效,而不是在版本系列仍处于预览期间。到 GA 时该版本系列被冻结,两条规则同时生效。

若某个版本系列在正式发布(GA)之前就已发布,必须在首次发布时声明其预览状态。

1.0 版本系列在 v1.3.0 参考工具发布时冻结。在这个系列内部,schema 变更只能是增量的,$id 保持在 v1.0 上;任何不兼容的变更都作为新的版本系列发布,绝不原地进行。

Schema#

  1. 每个 schema 携带形如 https://leji.org/schemas/v<major>.<minor>/<name>.schema.json 的稳定 $id。只有当 schema 的形态发生不兼容变更时,$id 的版本系列才移动。
  2. 在已发布的版本系列内部,schema 变更必须是增量的(新增可选字段)。删除字段或改变语义需要新的版本系列。
  3. 除清单以外的机器可读产物,通过 schemaVersion 声明它们所依据撰写的 schema 版本系列;清单则通过自命名的 leji 键声明它面向的规范版本系列(第 2 条)。

稳定集合#

以下内容在同一规范版本系列内被冻结;工具(包括未来的商业实现)据以构建,无需并行的 schema:

  • 清单的形态及其固定文件名 leji.json
  • 类别标识符(domainsystempracticegovernancedecisions),
  • 一致性级别标识符(coreindexedgovernedfederated),
  • machine-readable-surface.md 中的标识符与路径归一化规则,
  • 索引条目、变更日志条目、智能体配置与决策记录的形态。

实现工具(非规范性)#

各 SDK 和 CLI 分别按 SemVer 管理版本,并声明所支持的规范版本系列。本仓库中的参考 SDK 包括 npm 包 @leji-org/leji(packages/sdk)、PyPI 包 leji(packages/sdk-py),以及 Go 模块 leji(packages/sdk-go,单个静态二进制)。三者行为完全一致,并使用同一套共享 fixture 测试套件进行测试。