spec 1.0 · 规范性
Leji 上下文清单(leji.json)
AI 智能体辅助翻译。如有出入,以英文页面为准。如果你发现文本有任何问题,欢迎提交议题或发起拉取请求。
Leji 上下文层的机器入口。以 leji.json 的形式位于仓库根目录。
字段
- #
lejistring必填 本上下文层面向的 Leji 规范版本系列,例如 "1.0"。这个自命名的键沿用 OpenAPI 的惯例,用来表明该文件是一份 Leji 清单。
匹配模式
^\d+\.\d+$- #
namestring必填 本上下文层的一个简短、稳定的标识符,例如 "acme-billing-context"。
- #
descriptionstring 一行摘要,说明本上下文层涵盖什么。
- #
rootPathstring必填 上下文根目录,相对仓库根目录的 POSIX 路径。它声明上下文层位于何处,但不会为它所治理的路径重设基准:索引条目、侧边栏固定页面、配置路径,以及 Leji 产物中的其他所有路径,都从仓库根目录解析。查看器的
homepage、logo与favicon是例外:它们相对上下文根目录书写,落在该根目录之下的、相对仓库根目录的路径也会被接受。匹配模式
^(?!/)(?!\./)(?!.*(^|/)\.\.(/|$))(?!.*\\).*$- #
bootProfilePathstring必填 引导配置的路径,即每个智能体宿主和每个人都从这里开始的、不依赖具体智能体的入口。
匹配模式
^(?!/)(?!\./)(?!.*(^|/)\.\.(/|$))(?!.*\\).+\.md$- #
categoriesobject必填 逻辑类别到索引文件的映射。键是五个类别标识符;每个键映射到一份或多份撰写好的索引文件,由它们列出该类别的内容。
indexes字符串数组必填本类别的一份或多份索引文件,路径相对仓库根目录。每份索引文件都是撰写好的 markdown,其中带有一个
leji-index围栏代码块,块内的- path: <dir-or-file>行选出属于本类别的内容。内容仍留在原处;由索引文件声明纳入。domainobjectdomain 类内容所在之处:业务语言与产品语义。
systemobjectsystem 类内容所在之处:架构,以及每一次变更都要遵守的不变量。
practiceobjectpractice 类内容所在之处:约定与已被验证有效的模式。
governanceobjectgovernance 类内容所在之处:智能体护栏与运作规则。
decisionsobject决策记录所在之处:带日期的记录,说明事情为何如此。
- #
machineobject 机器可读产物的位置:索引、变更日志、智能体配置和决策记录。
indexPathstring生成的上下文索引的路径。
changelogPathstring机器可读的上下文变更日志的路径。
agentProfilesPathstring存放智能体配置文档的目录。
decisionRecordsPathstring存放决策记录的目录。
- #
agents映射 角色到智能体配置的绑定。键是角色标识符(例如 "thought-partner"、"reviewer");值是智能体配置文档的路径。协议启用角色,而这个映射决定由谁来担任。
- #
actors映射 可选的 actor 注册表,登记能够担任角色的参与者。键是稳定的 actor 标识符。每个 actor 声明它有资格担任的角色,以及按角色划分的命令模板。当某个角色有多于一个合格 actor,或者同一个 actor 因担任角色不同而需要不同调用方式时使用它;任一条件单独成立即足够。一个只有单一 actor、只需一条命令的角色,由智能体配置自身的 host 与 invocation 即可满足。
- #
ownersobject必填 谁对上下文层的健康负责。负责人要为上下文层保持当前有效、及时精简负责;但并不由他们独自撰写或编排(governance.md)。
namestring必填这个人的姓名。
contactstring如何联系到他们,例如一个电子邮件地址。
primaryobject必填对上下文层是否当前有效负责的那个人。没有负责人的上下文层会腐坏。
continuityobject可选。当主负责人无法履职或离开时,由另一个人承担同样的责任;有外部协助的接入应当在协助方撤出之前指定一位。重复填写主负责人不构成任何延续性,智能体也无法担任:负责人是承担责任的人。
- #
conformanceobject 本上下文层所声明的一致性级别,可由工具检查。
claimedLevel"core" · "indexed" · "governed" · "federated"所声明的一致性级别:core、indexed、governed 或 federated。
claimedAtstring最近一次作出该声明的 ISO 8601 日期。
- #
federationobject 挂载进本仓库的同级层(规范:distribution.md 模式 3)。圆环组合各方的归属,而不是把归属集中起来。
mounts对象数组每个挂载都是一个同级上下文层,通过解析器填充的层投影在固定版本处读取,并保有自己的仓库、归属和评审关卡。物化后的内容存放在被 git 忽略的解析器缓存中,绝不提交到这里。
namestring必填同级层的名称;必须与该同级层自身清单中的
name一致。sourcestring必填同级层的归一化仓库定位符:一个 https://、ssh:// 或 SCP 风格的远程 URL。它既是解析的根,也是固定版本的命名空间。本地文件系统路径只在机器本地的提示中有效,绝不出现在这里。
pinstring必填本宿主层所读取的同级层修订版的完整不可变提交 id(SHA-1 或 SHA-256)。它是保存在清单中的目标状态:固定版本的更新是有意为之、经过评审的编排。
trackingRefstring可选的、位于 source 上的全限定见证 ref(refs/heads/* 或 refs/tags/*),过期固定版本的报告与联邦可达性都相对它来比较。未声明时,使用 source 所公布的默认分支,并写入命令输出。
ownerobject必填同级层的负责人。归属留在同级层所属的团队;宿主层绝不吸收它的内容。
rolestring用散文说明这个同级层承载什么,例如 "product-side context"。它是给人读的;路由使用下面那些结构化字段。
categories由 "domain" · "system" · "practice" · "governance" · "decisions" 构成的数组这个同级层可以应答的内容类别范围。仅为路由元数据;同级层的内容仍留在同级层。federated 级一致性要求声明它。
topics字符串数组简短的、宿主层可见的主题标签,用于把一项任务路由到这个同级层,例如 "billing"、"checkout"。仅为路由元数据。
requiredWhen字符串数组在这些任务条件下,读取者必须解析这个挂载,无法解析时则停下并报告上下文不完整(失败即关闭)。它对宿主层可见;不得披露任何宿主层受众不该看到的内容。
- #
vendorAdapters字符串数组 本仓库中存在的厂商入口文件;每一个都必须重定向到引导配置。
- #
viewerobject 由
leji viewer读取的呈现偏好。这是非规范性的便利配置;呈现本身不属于规范范围。portintegerleji viewer serve首选的本地预览端口。--port 参数会覆盖它;默认值是 5354(手机键盘上的 LEJI)。logostring查看器使用的标志图片,以上下文根目录下的路径给出(例如 "assets/logo.svg")。默认使用 Leji 标志。
titlestring查看器的显示标题(侧边栏标题与页面标题)。默认使用上下文层的名称。
agentsLabelstring智能体配置分组在侧边栏中的标签(欢迎使用 emoji)。默认为 "🤖 Agents"。该分组列出本层的智能体配置,并像其他分组一样由 viewer.groupOrder 排序。
faviconstring查看器使用的站点图标,以上下文根目录下的路径给出(例如 "assets/icon.svg")。默认使用 Leji 标志。
homepagestring查看器的首页,以相对上下文根目录的路径给出(例如 "README.md")。默认使用脚手架生成的 overview.md。
pins元素数组固定在侧边栏顶部的页面:相对仓库根目录的 markdown 路径,或者用 {path, label} 指定一个自行编排的标签。固定引导配置会替换它默认的那一行。
groupOrder字符串数组自行编排的侧边栏分组顺序,按分组标签精确匹配(即索引文件的 H1)。列出的分组按此顺序排在前面;未列出的分组按推导顺序排在后面。
themeobject查看器主题的覆盖设置。
primarystring主色/强调色,用十六进制 CSS 颜色表示(例如 "#009F71")。它决定查看器外框、激活状态和图表强调色;正文链接与行内代码使用那个固定的无障碍色调,除非 viewer.theme.link 提供了一个能通过对比度检查的颜色。
linkstring链接颜色,用十六进制 CSS 颜色表示(例如 "#5A50F9")。只有当它相对行内代码背景(正文链接与行内代码所落在的两种背景中较窄的那一个)达到 4.5:1 时,才会应用到正文链接与行内代码;否则仍保留那个固定的无障碍色调,并由
leji view/leji export发出告警。mermaidboolean在查看器中把 ```mermaid 围栏代码块渲染为图表。默认为 true。
poweredByboolean在查看器角落显示那个小小的 "Powered by Leji" 标记。默认为 true;设为 false 即可移除。
categoryEmojisobject覆盖生成的层地图中每个类别旁默认显示的 emoji。(侧边栏分组的标签取自各索引文件自己的 H1。)
Schema 与示例
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://leji.org/schemas/v1.0/context-manifest.schema.json",
"title": "Leji context manifest (leji.json)",
"description": "The machine entrypoint of a Leji context layer. Lives at the repository root as leji.json.",
"type": "object",
"required": [
"leji",
"name",
"rootPath",
"bootProfilePath",
"categories",
"owners"
],
"additionalProperties": false,
"properties": {
"$schema": {
"description": "Optional pointer to this schema, for editor validation and tooling.",
"type": "string"
},
"leji": {
"type": "string",
"description": "The Leji spec line this context layer targets, e.g. \"1.0\". The self-naming key follows the OpenAPI convention and identifies the file as a Leji manifest.",
"pattern": "^\\d+\\.\\d+$"
},
"name": {
"description": "A short, stable identifier for this context layer, e.g. \"acme-billing-context\".",
"type": "string",
"minLength": 1
},
"description": {
"description": "One-line summary of what this context layer covers.",
"type": "string"
},
"rootPath": {
"type": "string",
"description": "Context root, POSIX path relative to the repository root. Declares where the context layer lives; it does not re-base the paths it governs: index entries, pinned pages, profile paths, and every other path in Leji artifacts resolve from the repository root. The viewer's `homepage`, `logo`, and `favicon` are the exception: they are written relative to the context root, and a repository-root-relative path under it is accepted too.",
"pattern": "^(?!/)(?!\\./)(?!.*(^|/)\\.\\.(/|$))(?!.*\\\\).*$"
},
"bootProfilePath": {
"description": "Path to the boot profile, the agent-agnostic entrypoint every host and person starts from.",
"type": "string",
"pattern": "^(?!/)(?!\\./)(?!.*(^|/)\\.\\.(/|$))(?!.*\\\\).+\\.md$"
},
"categories": {
"type": "object",
"description": "Logical category to index-file mapping. Keys are the five category identifiers; each maps to one or more curated index files that list the category's content.",
"additionalProperties": false,
"minProperties": 1,
"properties": {
"domain": {
"description": "Where domain content lives: business language and product semantics.",
"$ref": "#/$defs/categoryMapping"
},
"system": {
"description": "Where system content lives: architecture and the invariants every change respects.",
"$ref": "#/$defs/categoryMapping"
},
"practice": {
"description": "Where practice content lives: conventions and proven patterns.",
"$ref": "#/$defs/categoryMapping"
},
"governance": {
"description": "Where governance content lives: agent guardrails and operating rules.",
"$ref": "#/$defs/categoryMapping"
},
"decisions": {
"description": "Where decision records live: dated records of why things are the way they are.",
"$ref": "#/$defs/categoryMapping"
}
}
},
"machine": {
"description": "Locations of the machine-readable artifacts: index, changelog, profiles, and decision records.",
"type": "object",
"additionalProperties": false,
"properties": {
"indexPath": {
"description": "Path to the generated context index.",
"$ref": "#/$defs/relPath"
},
"changelogPath": {
"description": "Path to the machine-readable context changelog.",
"$ref": "#/$defs/relPath"
},
"agentProfilesPath": {
"description": "Directory holding agent profile documents.",
"$ref": "#/$defs/relPath"
},
"decisionRecordsPath": {
"description": "Directory holding decision records.",
"$ref": "#/$defs/relPath"
}
}
},
"agents": {
"type": "object",
"description": "Role to agent-profile binding. Keys are role identifiers (e.g. \"thought-partner\", \"reviewer\"); values are paths to agent profile documents. Protocols engage roles; this map decides who fills them.",
"additionalProperties": false,
"patternProperties": {
"^[a-z0-9]+(-[a-z0-9]+)*$": {
"$ref": "#/$defs/relPath"
}
}
},
"actors": {
"type": "object",
"description": "Optional registry of actors that can fill roles. Keys are stable actor identifiers. An actor declares the roles it is eligible for and a command template per role. Use it when a role has more than one eligible actor, or when one actor needs a different invocation depending on which role it is filling; either alone is reason enough. A role whose single actor needs only one command is served by the agent profile's own host and invocation.",
"additionalProperties": false,
"patternProperties": {
"^[a-z0-9]+(-[a-z0-9]+)*$": {
"$ref": "#/$defs/actor"
}
}
},
"owners": {
"type": "object",
"description": "Who is accountable for the context layer's health. Owners answer for the context layer staying current and pruned; they do not author or curate it alone (governance.md).",
"required": [
"primary"
],
"additionalProperties": false,
"properties": {
"primary": {
"$ref": "#/$defs/owner",
"description": "The person accountable for the context layer's currency. Ownerless context layers rot."
},
"continuity": {
"$ref": "#/$defs/owner",
"description": "Optional. A different person who carries the same accountability when the primary is unavailable or steps away; assisted adoptions should name one before outside help leaves. Naming the primary again provides no continuity, and an agent cannot fill it: owners are accountable people."
}
}
},
"conformance": {
"description": "The conformance level this context layer claims, checkable by tooling.",
"type": "object",
"additionalProperties": false,
"properties": {
"claimedLevel": {
"description": "The conformance level claimed: core, indexed, governed, or federated.",
"enum": [
"core",
"indexed",
"governed",
"federated"
]
},
"claimedAt": {
"description": "ISO 8601 date the claim was last asserted.",
"$ref": "#/$defs/isoDate"
}
}
},
"federation": {
"type": "object",
"additionalProperties": false,
"properties": {
"mounts": {
"type": "array",
"items": {
"type": "object",
"required": [
"name",
"source",
"pin",
"owner"
],
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"minLength": 1,
"description": "The sibling layer's name; must match the `name` in the sibling's own manifest."
},
"source": {
"type": "string",
"minLength": 1,
"description": "Normalized repository locator of the sibling layer: an https://, ssh://, or SCP-style remote URL. The resolution root and the pin's namespace. Local filesystem paths are valid only in machine-local hints, never here."
},
"pin": {
"type": "string",
"pattern": "^([0-9a-f]{40}|[0-9a-f]{64})$",
"description": "Full immutable commit id (SHA-1 or SHA-256) of the sibling revision this host reads. Manifest-held desired state: pin updates are deliberate, reviewed curation."
},
"trackingRef": {
"type": "string",
"pattern": "^refs/(heads|tags)/.+$",
"description": "Optional fully qualified witness ref on the source (refs/heads/* or refs/tags/*) that stale-pin reporting and federated reachability compare against. Absent, the source's advertised default branch is used and recorded in command output."
},
"owner": {
"$ref": "#/$defs/owner",
"description": "The owner of the sibling layer. Ownership stays with the sibling's team; the host never absorbs its content."
},
"role": {
"type": "string",
"description": "What this sibling layer carries, in prose, e.g. \"product-side context\". For humans; routing uses the structured fields below."
},
"categories": {
"type": "array",
"items": {
"enum": [
"domain",
"system",
"practice",
"governance",
"decisions"
]
},
"minItems": 1,
"description": "The content-category scopes this sibling can answer for. Routing metadata only; the sibling's content stays in the sibling. Required for federated conformance."
},
"topics": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"description": "Short host-visible topic labels used to route a task to this sibling, e.g. \"billing\", \"checkout\". Routing metadata only."
},
"requiredWhen": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"description": "Task conditions under which a reader must resolve this mount, or stop and report incomplete context if it cannot (fail closed). Host-visible; must not disclose anything the host's audience may not see."
}
}
},
"description": "Each mount is a sibling context layer read at a pinned version through a resolver-hydrated layer projection, keeping its own repository, ownership, and review gate. Materialized content lives in the gitignored resolver cache, never committed here."
}
},
"description": "Sibling layers mounted into this repository (spec: distribution.md pattern 3). The circle composes ownership; it doesn't centralize it."
},
"vendorAdapters": {
"type": "array",
"description": "Vendor entrypoint files present in this repository; each must redirect to the boot profile.",
"items": {
"$ref": "#/$defs/relPath"
}
},
"viewer": {
"type": "object",
"description": "Presentation preferences read by `leji viewer`. Non-normative convenience configuration; presentation itself is out of normative scope.",
"additionalProperties": false,
"properties": {
"port": {
"type": "integer",
"minimum": 1,
"maximum": 65535,
"description": "Preferred local-preview port for `leji viewer serve`. The --port flag overrides; the default is 5354 (LEJI on a phone keypad)."
},
"logo": {
"type": "string",
"description": "Logo image for the viewer, as a path under the context root (e.g. \"assets/logo.svg\"). Defaults to the Leji mark."
},
"title": {
"type": "string",
"description": "Display title for the viewer (sidebar header and page title). Defaults to the context layer name."
},
"agentsLabel": {
"type": "string",
"description": "Sidebar label for the agent-profiles group (emoji welcome). Defaults to \"🤖 Agents\". The group lists the layer's agent profiles and is ordered by viewer.groupOrder like any other group."
},
"favicon": {
"type": "string",
"description": "Favicon for the viewer, as a path under the context root (e.g. \"assets/icon.svg\"). Defaults to the Leji mark."
},
"homepage": {
"type": "string",
"description": "The viewer's landing page, as a path relative to the context root (e.g. \"README.md\"). Defaults to the seeded overview.md."
},
"pins": {
"type": "array",
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "object",
"additionalProperties": false,
"required": [
"path"
],
"properties": {
"path": {
"type": "string",
"description": "Repository-root-relative markdown path to pin."
},
"label": {
"type": "string",
"description": "Curated sidebar label for the pin (emoji welcome). Derived from the file when omitted."
}
}
}
]
},
"description": "Pages pinned at the top of the sidebar: repository-root-relative markdown paths, or {path, label} for a curated label. Pinning the boot profile replaces its default line."
},
"groupOrder": {
"type": "array",
"items": {
"type": "string"
},
"description": "Curated sidebar group sequence, by exact group label (the index file's H1). Listed groups come first in this order; unlisted groups follow in derived order."
},
"theme": {
"type": "object",
"additionalProperties": false,
"description": "Viewer theme overrides.",
"properties": {
"primary": {
"type": "string",
"description": "Primary/accent color as a hex CSS color (e.g. \"#009F71\"). Drives the viewer chrome, active states, and diagram accents; body links and inline code use the fixed accessible tone unless viewer.theme.link supplies one that clears the contrast guard."
},
"link": {
"type": "string",
"description": "Link color as a hex CSS color (e.g. \"#5A50F9\"). Applied to body links and inline code only when it reaches 4.5:1 against the inline-code ground, the narrower of the two backgrounds they land on; otherwise the fixed accessible tone stays and `leji view` / `leji export` warn."
}
}
},
"mermaid": {
"type": "boolean",
"description": "Render fenced ```mermaid code blocks as diagrams in the viewer. Defaults to true."
},
"poweredBy": {
"type": "boolean",
"description": "Show the small \"Powered by Leji\" mark in the viewer's corner. Defaults to true; set false to remove it."
},
"categoryEmojis": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"propertyNames": {
"enum": [
"domain",
"system",
"practice",
"governance",
"decisions"
]
},
"description": "Override the default emoji shown beside each category in the generated layer map. (Sidebar groups are labeled by each index file's own H1.)"
}
}
}
},
"$defs": {
"relPath": {
"type": "string",
"pattern": "^(?!/)(?!\\./)(?!.*(^|/)\\.\\.(/|$))(?!.*\\\\).+$"
},
"isoDate": {
"type": "string",
"pattern": "^\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])([T ].*)?$"
},
"owner": {
"type": "object",
"required": [
"name"
],
"additionalProperties": false,
"properties": {
"name": {
"description": "The person's name.",
"type": "string",
"minLength": 1
},
"contact": {
"description": "How to reach them, e.g. an email address.",
"type": "string"
}
}
},
"categoryMapping": {
"type": "object",
"required": [
"indexes"
],
"additionalProperties": false,
"properties": {
"indexes": {
"description": "One or more repository-root-relative index files for this category. Each index file is curated markdown carrying a fenced `leji-index` block whose `- path: <dir-or-file>` lines select the content that belongs to this category. Content stays where it lives; the index file declares inclusion.",
"type": "array",
"minItems": 1,
"items": {
"type": "string",
"pattern": "^(?!/)(?!\\./)(?!.*(^|/)\\.\\.(/|$))(?!.*\\\\).+\\.md$"
}
}
}
},
"actor": {
"type": "object",
"required": [
"roles",
"commands"
],
"additionalProperties": false,
"properties": {
"roles": {
"type": "array",
"minItems": 1,
"uniqueItems": true,
"items": {
"$ref": "#/$defs/roleId"
},
"description": "Role identifiers this actor is eligible to fill. Every entry must have a matching key in commands."
},
"commands": {
"type": "object",
"minProperties": 1,
"propertyNames": {
"$ref": "#/$defs/roleId"
},
"additionalProperties": {
"$ref": "#/$defs/actorCommand"
},
"description": "Command templates keyed by role identifier. The key set must equal roles. Keying by role is the point: one actor can need different invocations for different roles."
}
}
},
"actorCommand": {
"type": "string",
"minLength": 1,
"pattern": "<prompt>",
"description": "Command template used to engage this actor in the keyed role. Must carry the <prompt> placeholder, standing as its own unquoted shell word; the pattern verifies the substitution site exists, placement is the spec's rule."
},
"roleId": {
"type": "string",
"pattern": "^[a-z0-9]+(-[a-z0-9]+)*$",
"description": "A role identifier, lowercase and hyphen-separated, as used by the agents map."
}
}
}位于仓库根目录的一份符合规范的清单。
{
"$schema": "https://leji.org/schemas/v1.0/context-manifest.schema.json",
"leji": "1.0",
"name": "acme-billing-context",
"description": "Acme 计费平台 monorepo 的共享上下文层。",
"rootPath": "docs/",
"bootProfilePath": "docs/boot-profile.md",
"categories": {
"domain": {
"indexes": [
"docs/context/domain.md"
]
},
"system": {
"indexes": [
"docs/context/system.md"
]
},
"decisions": {
"indexes": [
"docs/context/decisions.md"
]
}
},
"machine": {
"indexPath": "docs/context-index.json",
"changelogPath": "docs/context-changelog.json",
"agentProfilesPath": "docs/agents/",
"decisionRecordsPath": "docs/decisions/"
},
"agents": {
"thought-partner": "docs/agents/thought-partner.md"
},
"owners": {
"primary": {
"name": "Acme Platform Team",
"contact": "platform@acme.example"
}
},
"conformance": {
"claimedLevel": "indexed",
"claimedAt": "2026-06-10"
}
}