# The Leji Specification **Leji is an open specification for the shared context layer of AI-native teams.** It defines how a team stores, governs, loads, and maintains the repo-owned context that people and AI agents both read on every task. | | | |---|---| | **Spec version** | 1.0.0 | | **Status** | GA, frozen at the v1.3.0 reference-tooling release. Breaking changes require a new major version. | | **Editor** | Vuong Nguyen | | **One page** | [The full specification on a single page](https://leji.org/spec/full/) | ## Principles (non-normative) 1. **Intent over instructions.** Leji captures durable intent (what things mean, what must hold, why it is so) instead of imperative, per-vendor instructions. People and agents derive actions from declared intent plus task context. 2. **A circle, not a tier.** Human-to-human, human-to-AI, and human-to-AI-to-human are first-class flows around one shared context layer. Equal access, not equal authority: everyone with access to a context layer reads all of it, anyone proposes, people approve. Participation is role-based, not tool-based: a participant who never touches git directly is first-class in the circle. Access itself is the version control system's to grant, not Leji's; the circle is scoped to a context layer's audience. 3. **Mechanism over goodwill.** Shared context decays by default: reality moves, documents don't, and nothing forces a wiki current. Leji's forcing functions are mechanical, not goodwill: changes ride the same review gate as code, tooling fails on mechanical drift, freshness horizons flag what has aged, and stale context is never silently treated as current (normatively, [governance.md](governance.md) → Freshness). The rest of this specification is the normative consequence of those three principles. ## Conformance language The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHOULD**, **SHOULD NOT**, **RECOMMENDED**, **MAY**, and **OPTIONAL** in this specification are to be interpreted as described in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119). ## Citing this specification (non-normative) Cite a section by its title and the spec version, with a permalink to the section anchor. On the spec site every heading reveals its anchor on hover. - **Format:** Leji 1.0, §_Section_: `https://leji.org/spec//#` - **Example:** Leji 1.0, §The circle, normatively: `https://leji.org/spec/governance/#the-circle-normatively` Always cite the version (`Leji 1.0`): breaking changes ship as a new major version, so a version-pinned citation stays accurate after the spec evolves. ## Vocabulary These terms are used consistently across all normative documents: | Term | Meaning | |---|---| | **context layer** | The artifact this specification governs: a repo-owned, versioned set of human-readable documents and machine-readable artifacts encoding a team's durable operating context. "Leji context layer" is the full disambiguating form. Always write "context layer"; bare "layer" is reserved for naming a countable federation instance (a sibling, host, mounted, restricted, companion, or inaccessible context layer). | | **agent** | An AI system that acts: it loads repository context, performs or assists work, and may propose changes. The normative actor noun. | | **person** / **people** | Human participants. People hold approval authority. | | **participant** | A person or an agent. | | **audience** | The people and agents admitted to read a context layer by its repository permissions and any filesystem or shared-drive permissions that expose the checkout. "Everyone reads" is scoped to a context layer's audience; different audiences are served by separate context layers, never by gating content within one. | | **agent host** | The product or runtime an agent operates through (for example Claude Code, Codex, Cursor). Vendor adapters configure agent hosts. | | **tool** | A callable capability an agent uses (shell, search, an MCP server). Never a product name. | | **vendor adapter** | An agent-host entrypoint file that redirects to the boot profile and never holds canonical content. Some are portable across hosts (`AGENTS.md`); others serve a single host (`CLAUDE.md`, `.cursor/rules`). The rule is the same for both; the difference changes only what tooling generates by default. | | **boot profile** | The agent-agnostic entrypoint of the context layer, for people and agents alike. | | **agent profile** | A role-specific loading-and-posture document for agents. | | **AI** | Used adjectivally (AI-native) and in the flow names **human-to-human**, **human-to-AI**, **human-to-AI-to-human**. In flow names, "AI" refers to agents operating through an agent host. | | **model** | The predictive engine an agent runs on. Models don't read the context layer; agents do. Appears only where the engine must be distinguished from the actor (for example, model selection as a host-specific mechanic). | The hierarchy, in one line: a **model** powers an **agent**; an **agent** operates through an **agent host** and calls **tools**; the context layer addresses agents and hosts, never models directly. The specification is agnostic at every level of that stack: any model, powering any agent, operating through any host, reading the same context layer. "LLM" is deliberately not part of this vocabulary: it names one class of model, and the specification is model-agnostic by the same principle. **Scope boundary.** Leji 1.0 governs agents and the agent hosts that load repository context. Non-agentic AI (autocomplete, inline suggestions, chat without repository context) is out of normative scope, except where it operates as part of an agent host that loads the context layer. ## Normative documents In reading order: | Document | Defines | |---|---| | [context-layer.md](context-layer.md) | The context layer, the manifest, the root, the vendor-adapter rule | | [content-categories.md](content-categories.md) | The five logical content categories and how index files map content to them | | [boot-profile.md](boot-profile.md) | The agent-agnostic entrypoint every agent host loads | | [machine-readable-surface.md](machine-readable-surface.md) | Manifest, index, changelog, profiles, decision records | | [decisions.md](decisions.md) | Decision records | | [governance.md](governance.md) | Propose/approve, ownership, inclusion and removal, freshness | | [distribution.md](distribution.md) | Monorepo, multi-repo submodule, federation | | [conformance.md](conformance.md) | The four conformance levels and the checklist | | [versioning.md](versioning.md) | Spec and schema versioning | The JSON Schemas in [`../schemas/`](../schemas/) are normative for the machine-readable artifacts. The documents in [`../rationale/`](../rationale/) and [`../adoption/`](../adoption/) are non-normative. ## Scope of 1.0 **In scope:** providing context, setting constraints, recording decisions, reviewing changes, and capturing reusable patterns; agent-agnostic wiring and vendor adapters (lightly); ownership and continuity semantics (lightly). **Extension boundary.** Leji 1.0 specifies the canonical shared context layer: how team context is written, owned, versioned, proposed, approved, indexed, and read. It deliberately does **not** specify the execution protocols that operate *around* that context layer: task envelopes, a generalized evidence protocol, agent-to-agent handoff, tool-permission protocols, and orchestration. These are **extension protocols, not prerequisites**: a conforming 1.0 context layer **MUST** remain useful without them, and an implementation **MUST NOT** require them to read, propose, review, approve, or validate the context layer. They complete the language as live practice proves them; they aren't invented in the abstract. Leji is **not** a programming language, a DSL, a runtime, or a SaaS. It is markdown conventions, small JSON schemas, and governance semantics. --- # The Context Layer A **Leji context layer** is a versioned, governed set of human-readable documents that encodes how a team thinks about its work: domain language, system invariants, conventions, guardrails, and decision records. People and agents both read it in the course of real work, and both propose changes to it through the same review gate. It lives under version control so that history, currency, and approval stay verifiable; the mechanics are in [Requirements](#requirements) below, and who takes part and how is in [Participation](#participation). ## Participation Participation in a context layer is **role-based, not tool-based**. Reading, proposing, reviewing, and approving happen through whatever interface preserves the repository's review and approval semantics; direct git or command-line knowledge is **not** required to take part. - **Everyone with access reads.** Access means practical access through the team's normal tools, not shell access to the repository. - **Anyone proposes; people approve.** A *proposal* is an intentional request to change the context layer. It **MAY** be authored directly by a person, generated by an agent from a person's request, or generated by an agent from observed work. In practice agents draft most context changes; the irreducibly human contribution is governance: proposing intent and approving what becomes canonical. A person approving a change is accountable for its meaning and consequence, not for personally operating the version control system. - **Human meaning, machine-readable surface.** The human-readable documents are the normative source of a team's operating context. The machine-readable files (manifest, index, changelog) exist so tools can locate, index, validate, and synchronize that meaning; they never replace it. The normative form of these flows, *the circle* (everyone reads, anyone proposes, people approve), is defined in [governance.md](governance.md). ## Reading a record Governed content comes in two kinds, defined in [content-categories.md](content-categories.md): **intent**, maintained as present truth, and **records**, dated evidence that later state supersedes rather than corrects. Both are equally governed; what differs is what a reader may do with what it loads. A reader **MUST NOT** treat a record as current intent: a record informs with the standing of evidence true within its stated boundary, and a reader that presents a record's claims as the present state of things, without saying so, is manufacturing currency the document does not have. This mirrors the degraded-mode rule below: in both cases, the reader's obligation is to know, and to say, what kind of currency it is holding. ## Requirements 1. The context layer **MUST** live in a git repository and **MUST** be versioned with the work it describes (same repo, or a dedicated context repository consumed per [distribution.md](distribution.md)). The git repository is what makes the context layer's history, checkout currency, and append-only changelog integrity verifiable; conforming tooling derives all three from it. Reading the context layer without that repository is a supported but degraded mode, defined in [Reading modes](#reading-modes-canonical-and-degraded). 2. A repository adopting Leji **MUST** carry a manifest file, `leji.json`, at the repository root, valid against [`context-manifest.schema.json`](../schemas/context-manifest.schema.json). The manifest is the machine entrypoint: it declares the spec version (the self-naming `leji` key), the context layer name, the context root, the boot profile path, the category mappings, an optional conformance claim, and ownership. It **MAY** also carry an `agents` map binding role identifiers (e.g. `thought-partner`, `reviewer`) to agent profile documents: protocols engage roles; the map decides who fills them. The map is a directory of roles, not a load order: a binding, including one at the `default` key, never causes a profile to be read; only the boot profile's Loading section does that. 3. The manifest **MAY** declare **actors**: named participants that can fill roles. Each actor declares the roles it is eligible for and a command template per role. Keying the command by role is the point: one actor can require a different invocation depending on which role it is filling, so a single command per actor cannot express it. An actor's declared roles and its command keys **MUST** be the same set. Where a role has actors, its bound agent profile **MUST NOT** also declare `invocation`: two authoritative commands with no stated precedence is a contradiction, and the layer resolves it by declaring the command in one place. Actors are optional and most layers need none. They earn their place when a role has more than one eligible actor, or when one actor needs a different invocation depending on the role it is filling; either alone is reason enough, and a role whose single actor needs only one command is served by the profile's own `host` and `invocation`. Declaring an actor grants no authority: it says who may be asked to fill a role, never who may approve. **Command templates**, wherever they appear (an actor's `commands` values and an agent profile's `invocation.command`), follow one rule. A template is a command line for the shell the invoker selects; engagement that is not shell-shaped (a structured argv call, an in-process spawn) is not representable by these fields in the 1.0 line. Every template **MUST** carry the `` placeholder, and each occurrence **MUST** stand as its own unquoted shell word in an argument position, never inside quotes or joined to other text. Substitution is one-pass: the occurrences present in the authored template are replaced simultaneously, exactly once, so the literal string `` inside prompt text stays data and is never re-expanded. The invoker owns delivery, and the required outcome, not the quoting algorithm, is the contract: each occurrence yields exactly one argument whose value equals the prompt text, with none of it evaluated as shell syntax. What the schemas verify is the presence of a substitution site; placement and delivery are this rule's to require and the invoker's to honor. 4. The manifest **MUST** declare a context root (`rootPath`). The **RECOMMENDED** default is `docs/`. All context layer paths are POSIX-style, relative to the repository root. `rootPath` declares where the context layer lives; it does **not** re-base other paths: every path in every Leji artifact resolves from the repository root, including those that repeat the `rootPath` prefix. Category and `machine` paths **SHOULD** fall under `rootPath`; validators warn when they don't. 5. The context layer **MUST** have a boot profile per [boot-profile.md](boot-profile.md). The **RECOMMENDED** default location is `docs/boot-profile.md`; the manifest's `bootProfilePath` declares the actual location. 6. Context layer content **MUST** be human-readable first. Markdown is the **RECOMMENDED** format for prose; structured metadata uses YAML frontmatter or the JSON artifacts defined in [machine-readable-surface.md](machine-readable-surface.md). A document that only a machine can read doesn't belong in the context layer. 7. The context layer **MUST** have a named owner (`owners.primary` in the manifest): a person accountable for its currency. Ownerless context layers rot. ## Reading modes: canonical and degraded A context layer is read in two modes; a reader **MUST** know which mode it is in, because the guarantees differ. A reader determines its mode from what it can resolve: a reachable `leji.json` at the repository root **and** either a git working tree or the host platform's repository revision identity is **canonical**; content reached as plain files with neither is **degraded**. 1. **Canonical.** The reader resolves the context layer through its git repository: a checkout, or the repository view of the host platform. History, checkout currency, and changelog integrity are verifiable, and approved content is known current to the revision read. 2. **Degraded.** The reader reaches the context layer as plain file content with no accessible git working tree or version metadata: files uploaded, synced, or copied into another interface without the repository. Plain-file reading is first-class *for reading* (the documents are human-readable by requirement, and the machine-readable changelog still conveys *declared* recency), but a degraded reader **MUST** treat checkout currency and approval state as **unknown**, never as current (see [governance.md](governance.md), Freshness). The changelog is the portable declared-recency surface in this mode; it does not by itself establish that the copy matches the canonical repository. Degraded reading widens who and what can consume a context layer; it is never a path to canonical authority. A change becomes canonical only through the git-backed review gate, and a degraded copy cannot satisfy the canonical-mode checks federation depends on (pin currency, stale-pin status, ownership intactness, and restricted-mount access per [distribution.md](distribution.md)). ## The vendor-adapter rule Agent-host configuration files (for example `CLAUDE.md`, `AGENTS.md`, `GEMINI.md`, `.cursorrules`, `.cursor/rules`, `.windsurfrules`, `.github/copilot-instructions.md`): 1. **MUST NOT** hold canonical context layer content. 2. If present, **MUST** redirect to the boot profile (typically a one-line pointer). 3. **MAY** carry host-specific mechanics that have no meaning outside that agent host (model selection, runner settings), provided no team knowledge lives there. 4. Tooling discovers which entrypoints to check from two sources: the manifest's optional `vendorAdapters` list, and a published well-known set (the files named above). The example list above is that well-known set for this line; a host whose entrypoint is not in it is checked only when the manifest names it in `vendorAdapters`. The entrypoint conventions that exist tell an agent host *where to look*; Leji defines *what the agent finds there*. One source of truth, every participant reading it. ## What the context layer is not (non-normative) - **Not a wiki.** Nothing forces a wiki current. The context layer stays alive because agents read it on every task (wrong context produces wrong output that is felt immediately), changes ride review like code, and tooling makes staleness visible. - **Not documentation in the traditional sense.** Documentation describes what the system does, after the fact. The context layer describes how the team thinks, in the present tense, and is read constantly by people and agents alike. - **Not a template to import.** Borrowed context goes stale immediately. The value of a context layer is that it is the team's own representation; Leji standardizes the shape and the governance, not the contents. --- # Content Categories Leji defines five **logical** content categories. They classify what a document is *for*, not where it lives: category names are stable identifiers used by the manifest, the index, and tooling. Directory names are the team's own. ## The five categories | Category | What belongs in it | |---|---| | `domain` | Business language and product semantics, in the team's own words: what the core nouns mean, how they relate, the terms with local meaning. Business-state records (an engagement status, a market snapshot) classify here too, as records. | | `system` | Architecture and its invariants: service boundaries, data ownership, integration contracts, consistency models, failure contracts, the constraints every change lives with. Technical assessments and system readouts classify here, as records. | | `practice` | Conventions and patterns applied automatically: code conventions, testing patterns, and the prompt and workflow patterns that have proven out (see capture gate below). Records of applying a method (a retro, a runbook execution log) classify here, as records. | | `governance` | Agent guardrails and operating rules: what agents may do unprompted, what needs a human gate, data-handling rules, escalation triggers, compliance controls. Governance evidence (an audit log, a review report) classifies here, as records. | | `decisions` | Dated records of why things are the way they are, per [decisions.md](decisions.md). | ## Intent and records Every governed document is either **intent** or a **record**, independent of its category: - **Intent** is maintained present truth: glossaries, invariants, conventions, guardrails. Readers rely on it as current, so when reality moves, the document is corrected. Intent is what review horizons and the freshness mechanism exist for (see [governance.md](governance.md)). - A **record** preserves claims within an explicit temporal or event boundary: statuses, assessments, ledgers, readouts, meeting outcomes, archives. Later state **supersedes** a record rather than correcting it; the original stays a valid account of its time. A record's currency surface is its **date**, never a review horizon. The classification test is one question: *if later information disagrees with this document, must the document be corrected because readers rely on it as current, or does the new information supersede it while the original remains a valid account of its time?* Corrected means intent; superseded means record. A record is governed exactly like intent: indexed, reviewed, owned, and routed. What differs is what a reader may do with it: a reader **MUST NOT** treat a record as current intent; it is dated evidence (see [context-layer.md](context-layer.md), Reading a record). Decision records are the formal record subtype: they are inherently records, with their own schema and lifecycle per [decisions.md](decisions.md). Some questions about records are deliberately out of 1.0 and acknowledged rather than hidden: there is no machine notion of a record *series* (so tooling never certifies which record is "the latest"), no stream-recency mechanism (whether the next expected record is overdue), and no section-level kinds for documents that materially mix intent and record content. A mixed document **SHOULD** be split; where splitting is disproportionate, classify by the contract downstream readers principally rely on. Content that honestly fits no category stays reference; classification is not promised to be judgment-free. ## Requirements 1. The manifest **MUST** map each category it claims to one or more repository-root-relative **index files** (`categories..indexes`); each index file **SHOULD** fall under the declared context root, per [context-layer.md](context-layer.md). An index file declares inclusion, it does not relocate: content stays where the team already keeps it (for example `business/`, `technology/`, `architecture/`), and one directory may contribute documents to more than one category without renaming anything. 2. An index file is curated markdown carrying one or more fenced `leji-index` code blocks. A block **opens** with a line of three or more backticks followed by the block's info string and **closes** with the next line of three or more backticks; the closing fence's backtick count need not match the opening fence's. Exactly three info strings are valid: `leji-index` (an intent block), `leji-index intent` (the same, explicit), and `leji-index record` (a record block, whose entries resolve as records). Any other token after `leji-index` is a parse error, never silently ignored: the grammar is finite by design. Each block lists content one entry per line as `- path: `, where a path is a directory (its markdown is included recursively) or a single markdown file. A path **MUST** be repository-root-relative POSIX: a leading `/`, a `..` segment, or a backslash is invalid and rejected. Blank lines and full-line `#` comments are ignored, and an entry **MAY** carry a trailing whitespace-preceded `# comment`. Whitespace in this grammar is ASCII space (U+0020) and tab (U+0009) and nothing else, everywhere the grammar consults it: around the fence backticks and the info string, as leading and trailing padding on an entry line, and before the `#` that opens a trailing comment. A leading UTF-8 byte order mark is stripped before parsing. Lines split on LF with a trailing CR tolerated, and the file is UTF-8. Implementations **MUST NOT** use a runtime whitespace class here: any other character a runtime happens to classify as whitespace, U+0085 and U+00A0 among them, is ordinary path content, so an entry whose path carries one is reported missing rather than silently trimmed. The `leji-mounts` blocks of [boot-profile.md](boot-profile.md) are frozen on the same alphabet, so one scanner reads both grammars and three implementations cannot disagree about whether a fence is even there. Multiple blocks in one file are concatenated in document order. Prose and headings around the blocks are allowed, so an index file doubles as a human-readable map of the category. The scan is line based and does not consult markdown structure: a line carrying three or more backticks and the tag, after optional space or tab indent, opens a real block wherever it sits in the document, including inside a longer fenced example or inside a list item. An example meant to illustrate rather than to declare is therefore fenced with a **different tag**, never with an extra token after `leji-index`: the tag is what the scanner matches on, so `leji-index example` opens a real block and reports a parse error, while a fence tagged `text` opens nothing. The **RECOMMENDED** location is `context/.md` under the context root; the location is configurable and the tooling never hardcodes it. 3. A context layer **MUST** map at least `domain` or `system`, plus `decisions`, to claim any conformance level (see [conformance.md](conformance.md)), and the populated `domain`/`system` minimum **MUST** include at least one **intent** document: a context layer of records alone preserves history but carries no operating context. The other categories accrete as the team hits real questions; an empty category (one whose index files resolve to no documents) **MUST NOT** be mapped to satisfy a checklist. 4. A document resolves to exactly one category and one kind. Index entries are **selectors**, and resolution follows **selector specificity**: a direct file selector beats any directory selector, and a deeper directory selector beats an ancestor directory selector. The most-specific selector covering a document determines its category and its block kind; a document a broader selector covers but a more-specific selector wins is simply not that broader selector's content (which is how one kept-current file inside a record directory, or one team's decision log inside a broader mapped tree, is expressed without moving anything). Selectors of **equal** specificity that disagree on category or kind are an error, never resolved by index order; identical equal-specificity assignments resolve once, while a literally duplicated entry within one index file is rejected. Tooling **SHOULD** surface a selector whose every covered document was won by more-specific selectors (a *shadowed* selector): dead weight in the curated map, never an error. Resolution is otherwise deterministic: a directory entry expands to its markdown in POSIX-lexical order (by Unicode code point; **RECOMMENDED** paths stay ASCII so order is unambiguous across implementations), and any path whose real location (after resolving symlinks) escapes the **repository root** is excluded rather than followed. Index entries (see [machine-readable-surface.md](machine-readable-surface.md)) carry the category identifier and the kind. 5. A document **MAY** declare its kind in frontmatter (`kind: intent` or `kind: record`); frontmatter overrides the winning selector's block kind and **never** the category. Any other `kind` value is an error. Decision records take no `kind` key (their schema is closed and they are inherently records). A record **MAY** carry a frontmatter `date` (`YYYY-MM-DD`); tooling reads a record's date **only** from that field, never from prose, header conventions, or filenames. A record **MUST NOT** carry `freshness.reviewAfter` (a review horizon is an intent mechanism; on a record it promises a currency the document cannot have, and it is an error). 6. Practice content describing prompt or workflow patterns **SHOULD** be captured only after the pattern has worked at least twice (the proven-twice gate). Premature capture is how practice directories fill with aspiration. ## Notes (non-normative) Not every category is present on day one. The minimum viable context layer is whatever the first month of work actually relies on. Categories exist so that a human or an agent can ask "what kind of truth is this?" and load the slice that matters for the task at hand, instead of the whole tree. The two kinds exist because a real repository's documentation is two interleaved corpora with different truth models, and forcing the operational half under intent semantics fails both ways: freshness promises that cannot be honored, or the majority of the repository exiled outside governance. A worked shape, with one intent exception inside a record directory: ````markdown # 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 ``` ```` --- # The Boot Profile The **boot profile** is the agent-agnostic entrypoint of the context layer: one human-readable document that every agent host, and every person, can start from. It answers "what is this context layer, what do I load, and how do I behave here." ## Requirements 1. The context layer **MUST** have exactly one boot profile, located at the path declared by `bootProfilePath` in the manifest. The **RECOMMENDED** default is `docs/boot-profile.md`. 2. The boot profile **MUST** be plain markdown, readable by a person with no tooling. It **MUST NOT** depend on any vendor's configuration syntax. 3. The boot profile **MUST** cover: - **Identity**: what this repository or product is, in a paragraph. - **Loading**: what context to read for which kind of task. This **MUST** give an unconditional set (what to read before any task), then task-typed selectors that route by path, by category, or via the context index, and a defined fallback for a task that matches no selector. Stated in task language, this is the boot-profile-level expression of the Task routing algorithm ([machine-readable-surface.md](machine-readable-surface.md)); following it requires no knowledge of that algorithm. - **Posture**: the agent's operating expectations (when to proceed, when to ask, what to never do). This **MAY** be carried by reference to governance content or a core agent profile. 4. The boot profile **SHOULD** link to the manifest, the index (if present), and the agent profiles (if present), so that an agent entering through any host can discover the whole machine-readable surface. 5. The boot profile **MUST** speak task language: it names literal paths and concrete load order, and following it requires no knowledge of this specification. The manifest and schemas exist for tooling, not for agents; a boot profile that requires spec literacy to follow is a conformance smell. 6. The boot profile **SHOULD** state the context layer's maintenance duties: where its changes are recorded (the declared changelog) and how decisions are captured (the declared decision-records location). Validators warn when the boot profile references neither. 7. Vendor entrypoint files redirect to the boot profile per the vendor-adapter rule in [context-layer.md](context-layer.md). 8. The boot profile's unconditional load set (what it says to read before any task) **SHOULD** be bounded to what every task needs. Context only some tasks need **SHOULD** be routed by task, category, or the index rather than preloaded; and decision records **SHOULD** be routed by their declared `affectedPaths` / `affectedCategories` rather than loaded as a whole directory, since they accrue without bound. Everything in the unconditional set is paid on every task. 9. **Federated siblings.** A context layer that declares `federation.mounts` (per [distribution.md](distribution.md)) **MUST** surface those siblings in the boot profile in a machine-checkable form: one or more fenced blocks whose info string is `leji-mounts`, placed anywhere in the document, whose entries concatenate in document order and carry exactly one entry per declared mount. An entry names the sibling, its owner, what it carries, and when to read it, the last two in the author's task language. The worked example is below the requirements. The grammar is fixed so every implementation reads it identically. A block **opens** with a line of three or more backticks followed by the info string and **closes** with the next line of three or more backticks; the closing fence's backtick count need not match the opening fence's. The info string is `leji-mounts` alone; a fence carrying any token after it is an error, never an ignored fence. The fence lines **MAY** carry space or tab indent and padding, and the records between them **MUST NOT**: a record begins at column 1 with `- mount: `, and its fields are indented exactly two ASCII spaces. Within a record `owner`, `carries`, and `read-when` each appear exactly once, in any order; unknown fields, duplicate fields, and missing fields are errors. A value is the nonempty remainder of its line after the `key: ` prefix, with no leading or trailing space or tab and no control or line-separator character. Whitespace in this grammar is ASCII space (U+0020) and tab (U+0009) and nothing else, in the fence line's indent and padding as much as in a content line's; implementations **MUST NOT** use a runtime whitespace class here, since those disagree about characters such as U+0085 and U+00A0 and would disagree about whether a block exists. A leading UTF-8 byte order mark is stripped before parsing. Lines split on LF with a trailing CR tolerated, blank lines and full lines starting `#` are ignored (as in the category index blocks of [content-categories.md](content-categories.md)), and the file is UTF-8. The scan is line based and does not consult markdown structure: a line carrying three or more backticks and the tag, after optional space or tab indent, opens a real block wherever it sits in the document, including inside a longer fenced example or inside a list item. An example meant to illustrate rather than to declare is therefore fenced with a **different tag**, never with an extra token after `leji-mounts`: the tag is what the scanner matches on, so `leji-mounts example` opens a real block and reports a parse error, while a fence tagged `text` opens nothing. `mount` **MUST** match a declared mount's `name` and `owner` **MUST** match that mount's declared `owner.name`, compared as decoded strings; an entry for an undeclared mount, a second entry for one mount, and a declared mount with no entry are all errors. A layer that declares no mounts **MUST NOT** carry a `leji-mounts` block. The sibling's location is deliberately not an element: a mount is materialized in a machine-local, content-addressed projection, so a reader resolves it with `leji mounts locate ` rather than inferring a path (per [distribution.md](distribution.md)). Prose around the block **SHOULD** explain the routing naturally; the block is the checkable core, never a replacement for that prose or for the declaration in `leji.json`. Mounted siblings are distinct, named sources, never merged into the host's categories; the boot profile routes the agent into a sibling only when the task matches its routing or the profile requires it. What stays unchecked is deliberate: `carries` and `read-when` are free text, and their fidelity to the mount's routing metadata is attested by the team rather than verified by tooling, which checks enumeration, identity, and presence. Surfacing siblings here keeps mount discovery in the agent's task-language entrypoint, so following requirement 5 still needs no manifest reading. ### A worked `leji-mounts` block One entry, for a host that declares a single mount named `acme-product-context`. The block sits at column 1 in the boot profile, exactly as it reads here; the outer four-backtick fence is this document's wrapper and is not part of it. ````markdown ```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 ``` ```` ## Agent profiles A context layer **MAY** define role-specific profiles (for example a reviewer profile, a release profile, a QA profile) under a directory declared by `machine.agentProfilesPath`. Each profile: 1. **MUST** be markdown with YAML frontmatter valid against [`agent-profile.schema.json`](../schemas/agent-profile.schema.json). 2. **MUST**, once inheritance is resolved, carry what the role reads first (`requiredRead`) and when it must stop and ask (`mustAskWhen`). A profile that declares `inherits` **MAY** omit either one where its base supplies it; a profile that does not **MUST** declare both itself. 3. **MAY** declare `inherits`, which is operative in the 1.0 line: it names exactly one other profile in the layer's profile set, whose `role` **MUST** be `core`, and whose posture and body this profile extends. The layer's profile set is every document under the declared `machine.agentProfilesPath` together with every document named in the manifest's `agents` map, wherever that document sits. Resolution is single level, so a profile whose `role` is `core` **MUST NOT** declare `inherits`, and the named target **MUST** exist, **MUST** be unique by `id`, and **MUST NOT** declare `inherits` itself. Resolution composes: - **Posture arrays** (`requiredRead`, `defaultContext`, `mustAskWhen`, `mustRefuseWhen`): the base's entries in their authored order, then the derived profile's entries in theirs, dropping any the base already carries. Authored order is loading intent, so nothing is sorted. - **Every other field** (`id`, `name`, `role`, `purpose`, `version`, `host`, `invocation`, `escalation`, `owners`, `freshness`): the derived profile's own, never inherited. `inherits` is a resolution directive and is not itself part of the resolved profile. - **Body**: both bodies are normative, the base's first, then the derived profile's. A consumer that cannot resolve an inherited profile **MUST NOT** apply the derived file on its own; the derived file is one half of a profile, so the consumer reports it unsupported instead. Where an ask condition and a refuse condition both apply to the same situation, refusal governs. Resolution guarantees composition, not semantic narrowing: derived prose that contradicts or weakens the base is nonconforming, and no tooling detects a natural-language contradiction. Profiles tune *what a role loads and how it behaves*; they don't duplicate context layer content. A profile's optional `host` and `invocation` are the single-actor shorthand: they say how to engage the one participant that fills this role. Its `command` is a template following the same rule as actor command templates, including the `` placeholder and its placement (see [context-layer.md](context-layer.md), Requirements). Where a role has more than one eligible participant, or where the same participant needs a different invocation depending on which role it is filling, the manifest's optional `actors` registry carries that instead (same section). A role uses one mechanism or the other, never both. ## Notes (non-normative) The boot profile is deliberately boring: a map and a posture, not a knowledge base. If the boot profile grows past a few screens, content is living in the entrypoint that belongs in a category. The failure mode this design guards against is indirection: every hop between an agent's first context and the actual constraint costs attention. A context layer implemented well needs no vendor entrypoints at all (invocation can point straight at the boot profile), and the boot profile walks straight to content. Depth belongs in the context layer's documents, never in the path to them. Every document the boot profile says to read before any task is paid on every task, so the unconditional set is the context layer's most expensive space. Keep it to what is genuinely universal, and route the rest through task-typed loads, the categories, the index, and the scope each decision record declares. The index exists so an agent can load the slice a task needs instead of the whole tree; decisions accrue without bound, so they are routed, never preloaded as a directory. --- # The Machine-Readable Surface Five artifacts make the context layer legible to tooling. Everything else in the context layer is prose for humans that agents happen to read; these five are the contract tools build against. | Artifact | Default location | Schema | |---|---|---| | Manifest | `leji.json` (repository root, fixed) | [`context-manifest.schema.json`](../schemas/context-manifest.schema.json) | | Context index | `/context-index.json` | [`context-index.schema.json`](../schemas/context-index.schema.json) | | Context changelog | `/context-changelog.json` | [`context-changelog.schema.json`](../schemas/context-changelog.schema.json) | | Agent profiles | `/agents/*.md` (frontmatter) | [`agent-profile.schema.json`](../schemas/agent-profile.schema.json) | | Decision records | `/decisions/*.md` (frontmatter) | [`decision-record.schema.json`](../schemas/decision-record.schema.json) | All locations except the manifest are manifest-declared; the table shows defaults. ## Requirements 1. **Manifest.** `leji.json` **MUST** exist at the repository root and validate against its schema. It is the only fixed filename in Leji: the file tooling reliably looks for. 2. **Index.** A context layer claiming `indexed` conformance or above **MUST** carry a context index that is **generated, never hand-maintained**: tooling resolves the category index files (`categories..indexes`, per [content-categories.md](content-categories.md)) to the documents they list and writes one entry per governed document. Each entry carries a stable `id`, a `path`, a `title`, and a `category` identifier. A generator **SHOULD** also emit the document's `kind` (`intent` or `record`); it is optional in the schema so an index written before kinds existed stays valid, and a consumer treats an absent value as `intent`. A record's entry additionally carries its `date` when the document declares a valid frontmatter `date`; the generator sources dates from frontmatter only, never from prose or filename conventions. A stale index (one that no longer matches what the index files resolve to) **MUST** be treated as a validation failure. A host that declares `federation.mounts` also carries, in the same index, a top-level `mounts` array: one routing record per mount (`name`, `source`, `pin`, `trackingRef` when declared, `owner`, `role` when declared, and the routing metadata `categories` / `topics` / `requiredWhen`). These are routing records only; tooling **MUST NOT** copy a sibling's entries or prose into the host index, and a mount record carries nothing the host's audience may not see (per [distribution.md](distribution.md), Restricted mounts). 3. **Changelog.** A context layer claiming `indexed` conformance or above **MUST** carry a machine-readable changelog of context layer changes. Entries carry a stable `id`, a UTC `date`, a `type`, a one-line `summary`, and the affected `paths`. Canonical order is **derived, not positional**: tooling **MUST** order entries by `(date, id)` ascending, and array position carries no meaning. Because `id` is unique within the changelog (Identifiers), `(date, id)` is a total order even when two changes share a `date`. Surviving entries are immutable: tooling **MUST** treat modification of a published entry as a validation failure wherever it can establish the prior state, and reordering the array is not a modification. Establishing that state needs a distinct baseline to compare against; where the reference tooling has only the current revision, as in an ordinary continuous-integration checkout, the modification is not visible to it and the review of the change set is what catches it (see [conformance.md](conformance.md)). The changelog is a **recency surface, not an archive**: a long-lived context layer **SHOULD** compact it rather than let it grow without bound, and **MAY** compact it at any time by removing entries from the oldest end of that order, provided the same change set appends an entry of type `compaction` whose `compacted` field records the count and the first and last removed ids. Removal of anything but the oldest entries, removal without a compaction entry, and compaction to an empty file are validation failures. Append-only discipline is **set-keyed by `id`** and checked against the prior committed state, so it needs git at authoring time; the file itself stays git-free for consumers, and git history holds the full record. A change set that touches **governed documents** (those the category index files resolve to) **MUST** append an entry whose `paths` cover the governed paths it changed, so every changed governed document falls under some appended entry: append-only discipline keeps published entries immutable, and this coverage rule keeps the record complete. A human-readable changelog **MAY** exist alongside; the JSON record is the one tooling reads. 4. **Frontmatter artifacts.** Agent profiles and decision records are markdown documents whose YAML frontmatter validates against their schemas. The prose body stays free-form; the frontmatter is the machine contract. Pure-JSON profiles or decisions **MUST NOT** be required: people read these documents. 5. **Identifiers.** All `id` values **MUST** be stable once published: renames and moves update `path`, never `id`. Identifiers are lowercase, hyphen-separated, unique within their artifact type. A generated index entry's `id` is **derived** in priority order: the document's frontmatter `id` if it declares one; else the `id` the stored index already carries for that same path, or, for a pure content-preserving move, for that same content; else a slug of the filename, de-collided against its parent directory. The first that exists wins, so a published `id` survives a rename or a move and only a brand-new document mints a fresh one. A document that might be moved **and** edited in one change set **SHOULD** declare a frontmatter `id`: only frontmatter pins the id through a simultaneous path and content change (the carry-by-path and carry-by-hash fallbacks both miss it), and tooling warns (`id-vanished`) when a stored id disappears so the dangling references it leaves are caught. 6. **Timestamps.** Changelog `date` values are ISO 8601 in **UTC**: either a calendar date `YYYY-MM-DD` (ordered as that day's start, `T00:00:00Z`) or a whole-second timestamp ending in `Z` (for example `2026-06-13T15:04:05Z`). Zoneless times, non-UTC offsets and fractional seconds are **not** permitted: fractional seconds would break the guarantee that a lexical sort of `date` is a chronological sort, since `…05.1Z` sorts before `…05Z` while being later. Every date field in every artifact is calendar-ranged, so a month of `13` or a day of `99` is invalid. Other artifacts' dates follow ISO 8601 and **MAY** be date-only. **Paths** are POSIX-style, relative to the repository root, no leading `./`. 7. Every JSON artifact except the manifest **MUST** declare the schema line it was written against (`schemaVersion`), per [versioning.md](versioning.md); the manifest declares its target spec line with the self-naming `leji` key. 8. **Derived surfaces inherit access constraints.** The index, the changelog, the generated viewer, and any compiled or exported view built from context layer content are *derived surfaces*, as is the output an agent produces from that content. A derived surface carries the access constraints of the most restricted content it draws from. A derived surface **MUST NOT** be written or copied to a location with a broader audience than that content without an explicit, reviewed redaction step that produces a separate surface for that audience, and an agent **MUST NOT** quote or summarize restricted context into a broader-audience or less-restricted surface (a pull request, ticket, chat, commit message, or public context layer). The index of a restricted context layer can be as sensitive as its prose: titles, paths, and summaries all describe it. This is a constraint on the people and agents who operate the tooling, not a check the tooling performs: Leji defines no audience model a tool can read to compute "broader audience" (access is the version control system's, per [governance.md](governance.md), Access boundary), so the reference SDK does not enforce it and the most any tool does is warn (the viewer export warns to host it privately). ## Task routing The index, the category assignments, and the decision records exist so an agent can load the slice of context a task needs instead of the whole tree. This section defines, normatively, how a task's scope selects that slice. It is the one routing algorithm the rest of the specification refers to: the boot profile's Loading section ([boot-profile.md](boot-profile.md)) points an agent here in task language, decision-record scope ([decisions.md](decisions.md)) is matched by it, and federated reading ([distribution.md](distribution.md)) reuses it to decide which siblings a task touches. Routing reads context; it is not a task envelope or an execution protocol, which stay outside 1.0 (see [README.md](README.md), Extension boundary). 1. **Input.** A task's scope is the set of repository-root-relative POSIX paths the task reads or changes (normalized per Requirement 6: POSIX-style, root-relative, no leading `./`), together with any categories the task explicitly names and any topics the task explicitly names. Topics are explicit inputs: the algorithm never derives them from paths, categories, prose, or content. How an agent or tool derives the scope from the task is out of normative scope; the matching below is not. 2. **Path match (lexical, bidirectional).** A declared path and a task path match when, after normalization (POSIX-style, root-relative, no leading `./`, any trailing `/` removed), the two strings are equal, or one is a path-prefix ancestor of the other: the shorter equals the longer truncated at a `/` boundary. Matching is **purely lexical**: it never consults the filesystem, and it draws no distinction between a path that names a file and one that names a directory, because after normalization the two are indistinguishable. This is containment in either direction (the `underPath` relation the reference implementations share), so a broadly-scoped task and a narrowly-declared selector find each other whichever side is broader. 3. **Category match (narrow), and the two category sets.** A task's categories divide into **expanded** and **signalled**. A category the task **explicitly names** enters both sets. A task path that is **itself a governed document** (matching its generated index entry by *exact equality*, never by containment) contributes that entry's category to the **signalled** set only. **Expanded** categories load their intent documents and record candidates; **signalled** categories are a matching signal for decisions and federation mounts and load nothing on their own. A category selector **MUST NOT** infer a category for an arbitrary repository file, and a task path that is not itself a governed document, including any ancestor directory of one, contributes no category at all. Path scope reaches files; category expansion does not follow it. 4. **Topic match (exact, mount-only).** A topic is a non-empty string of Unicode scalar values, compared by its UTF-8 encoding; a lone surrogate is not a valid topic. Both sides are held to that rule: a task topic or a mount `topics` entry that is not a non-empty string of Unicode scalar values is an input error, and an implementation **MUST** reject it rather than return it as a silent non-match. A task topic matches a declared topic when the two decoded strings are **exactly** equal. Implementations **MUST NOT** case-convert, Unicode-normalize, locale-compare, trim, tokenize, substring-match, or fuzzy-match either side, so canonically equivalent spellings that differ in bytes do not match; this equality rule is separate from the bytewise **ordering** of results below. Duplicate task topics form one signal, so naming a topic twice matches exactly as naming it once does. A federation mount matches when any task topic equals any topic the mount declares. A topic match selects the **mount only**: it **MUST NOT** enter the expanded or signalled category sets, load any document or record, route any decision, evaluate `requiredWhen`, or make a mount required. 5. **Status filter.** Only a decision record whose `status` binds is routed as current guidance. `accepted` and `deprecated` bind; a `deprecated` record binds with a stale posture, and an agent **MUST** treat it as guidance on its way out rather than settled current practice. A `superseded` record **MUST NOT** bind except as history and **MUST** carry `supersededBy`; `proposed` and `rejected` records **MUST NOT** bind. A record that binds is *live*. 6. **Unscoped decisions.** A live decision record that declares neither `affectedPaths` nor `affectedCategories` is org-wide: it is routed for every task, whatever the task's scope. Scoped live decisions are routed only when the task matches them by path (2) or category (3). 7. **Empty path scope, and empty scope.** When the task's **path set** is empty, path matching contributes nothing and an agent **MUST** state that path-scoped routing was not evaluated; explicitly named categories are still honored and still expand, and explicitly named topics are still matched. Named categories and named topics both count toward a non-empty scope. The task's **whole scope** is empty only when it names no paths, no categories, and no topics; then an agent routes only the unconditional boot-profile and agent-profile context plus the org-wide unscoped live decisions. An agent **MUST NOT** present an unrouted load as though it were scoped. 8. **Records route as candidates.** An **expanded** category routes its **intent** documents as required context; that category's **records** are returned separately, each with its kind and date, as candidates the reader loads by judgment. A record becomes **required** only when the task's paths select it directly under item 2, an agent or boot profile names it, or a person asks for it; being a category match, or carrying the newest date, never makes a record required. Where a record is both a category candidate and directly path-selected, the direct selection wins and it is required. Routing **MUST NOT** certify any record as "the latest" or "the current one": 1.0 defines no record-series identity or ordering guarantee, so recency judgments belong to the reader, made against the dates the index surfaces. Decision records keep their own routing (items 5 and 6) and never route as generic records. Naming a decision file as a task path does not route that decision; its declared scope does. 9. **Citation.** An agent that loads routed decision records **MUST** cite which matched records it loaded, so a reader can see which guidance the agent applied and infer what it did not. **The routed slice** is what an agent loads for a task. It is the union of: the boot profile's unconditional load set and the active agent profile's `requiredRead`, which the agent holds as its **baseline** independent of any scope; every governed intent document in an **expanded** category; every governed entry the task's paths select under item 2; every record directly path-selected under item 8; and every live decision the task matches by path or by a **signalled** or expanded category, plus the org-wide unscoped live decisions. **Signalled** categories contribute matching only, to decisions and to federation mounts, and never expand a corpus. **The slice is not the envelope.** A tool that computes routing returns the slice alongside material the agent must **not** load unprompted: record candidates, and the routed-candidate metadata around them. Loading the envelope wholesale defeats the point of routing. **Result ordering is normative** where a tool emits one, so independent implementations agree byte for byte: categories in the canonical category order of this specification; documents, records and decisions by ascending path; mounts by ascending name. String comparison is **bytewise over UTF-8**, not locale- or codepoint-collation dependent. Tooling **MAY** offer a helper that computes this slice from a path set; the reference implementations expose one (`route`). Such a helper computes the **scope-dependent portion** and is not required to emit the baseline, which the caller already holds; the agent's obligation to load that baseline is unchanged. Routing conforms whenever a raw reader follows this algorithm. ## Notes (non-normative) The reference tooling checks the changelog's schema and append-only discipline today (`leji validate` runs both); verifying changelog *coverage* against a base revision, that every changed governed path appears in an appended entry, is a reported check on the roadmap, not yet a blocking gate. Until it ships, coverage rides the process-attested review and CI discipline (see [conformance.md](conformance.md)). The index is the navigation source for the governed context: `leji viewer` renders that governed spine from the index, and below it the repository's own directory tree as a browsable reference zone, so one view surfaces both the governed context and the team's existing navigation. Any docs tool can project the index the same way. Presentation is non-normative. The surface is deliberately small. Five shapes are enough for tooling to validate a context layer, diff it, score its freshness, and route an agent to the right slice, and few enough that a team can hold the whole surface in mind. Anything beyond these five is post-1.0 territory, gated on lived practice. --- # Decisions Decision records are the context layer's dated account of *why*: architecture decisions, vendor selections, scope boundaries, intentional non-decisions. They stop re-litigation, and they give agents the reasoning rather than just the rule. Decision records are the formal **record** subtype (see [content-categories.md](content-categories.md), Intent and records): inherently records, with a uniform schema and lifecycle generic records do not have. Their generated index entries carry `kind: record`; a decision record declares no `kind` key of its own (the schema is closed, so an explicit `kind` fails validation). ## Requirements 1. Decision records are markdown with YAML frontmatter valid against [`decision-record.schema.json`](../schemas/decision-record.schema.json), one record per file. The decision corpus is the union of two manifest-declared surfaces, and a context layer **MAY** use either or both: the declared records path (`machine.decisionRecordsPath`, default `/decisions/`) and the entries the `decisions` category's index files resolve to. A record **MUST** be reachable through at least one of the two. 2. Frontmatter **MUST** carry: `id` (stable), `title`, `status`, and `date`. `status` is one of `proposed`, `accepted`, `superseded`, `deprecated`, `rejected`. 3. The body **MUST** state, in prose: the context (what situation forced a decision), the decision itself, and its consequences. The **RECOMMENDED** section headings are `## Context`, `## Decision`, `## Consequences`; a record **MAY** add `## Alternatives`. 4. Records are **append-only history**: a record **MUST NOT** be edited into a different decision. Two frontmatter fields are **mutable** as a decision ages, `status` (its lifecycle) and `supersededBy` (set when it is superseded); everything else, the `id`, the original `title` and `date`, the declared scope, and the prose body, is **immutable** once published. A reversal or change is a new record whose frontmatter sets `supersedes`, and the old record's `status` becomes `superseded` with `supersededBy` set. The supersession link **MUST** stay consistent in both directions: when record B sets `supersedes: A`, record A carries `status: superseded` and `supersededBy: B`, and a `superseded` record **MUST** name its successor in `supersededBy`. Both records remain. The reference tooling enforces the bidirectional supersession consistency today. It does not yet verify immutability itself (that a published record's frozen fields and body are unchanged against a base revision); that is a reported check on the roadmap, not yet a blocking gate. Until it ships, immutability rides the process-attested review discipline (see [conformance.md](conformance.md)). 5. A record **MAY** declare `affectedPaths` and `affectedCategories`, so tooling can route from a task's scope to the decisions that govern it. How a task's scope selects records (overlap-aware path containment, narrow category match, the `accepted` / `deprecated` bind, and the org-wide treatment of a record that declares neither) is the Task routing algorithm in [machine-readable-surface.md](machine-readable-surface.md). 6. Rejected proposals are records too (`status: rejected`). A decision not taken, written down, is the cheapest re-litigation insurance there is. ## ADR compatibility (non-normative) Leji decision records are deliberately compatible with Architecture Decision Records: an existing ADR directory satisfies `decisions` by adding the frontmatter fields to each record (or to new records going forward) and mapping the directory in the manifest. No ADR tooling is required, and none is excluded. --- # Governance Governance is what separates a context layer from a wiki. The semantics are the circle: equal access, not equal authority. ## The circle, normatively 1. **Everyone reads.** All participants, people and agents alike, with access to a context layer **MUST** be able to read the whole of it. A context layer with role-gated reading *within* it isn't a shared context layer; where different people may read different material, that material belongs in separate context layers (see [Access boundary](#access-boundary) below and [distribution.md](distribution.md)). 2. **Anyone proposes.** Any participant, person or agent, **MAY** propose changes to the context layer. Agent-authored proposals are first-class: an agent that discovers missing or wrong context while working **SHOULD** propose the fix in the same change set as the work that surfaced it. A proposal **SHOULD** carry rationale sufficient for a reviewer to understand its intent and expected effect; that rationale is the minimum a person needs in order to approve. Leji 1.0 defines no generalized evidence protocol (see [Scope of 1.0](README.md)). 3. **People approve.** Every change to the context layer **MUST** be approved by a person before it becomes canonical. Approval rides the repository's existing review mechanism (pull requests); Leji introduces no separate process. Participation **MAY** happen through any interface, but **canonical approval MUST be an auditable review record in that mechanism**: attributable to the approving person and bound to the change set under review. An approval expressed only in external discussion, chat, ticket state, or document comments does not count until it becomes such a record; mirroring it into a comment does not. Widening how people take part never moves where authority is recorded. ## Requirements 4. **Ownership, not authorship.** The manifest **MUST** name a primary owner (`owners.primary`) and **MAY** name a continuity owner (`owners.continuity`): a *different* person who carries the same accountability when the primary is unavailable or steps away. An assisted adoption **SHOULD** name the continuity owner before outside help leaves; a solo context layer **MAY** have none, which honestly signals it has no succession. Owners are accountable **people**: an agent proposes and reviews but never owns, and naming the primary again as continuity provides none. The owner is accountable for the context layer's *health*: that it stays current, that stale or contradictory content gets pruned, and that every area has someone who tends it. **Owners are not curators.** The content is written and kept true by the whole circle as it works; concentrating that in one keeper is the bottleneck this model exists to avoid. 5. **Review scope.** Context layer changes **SHOULD** be reviewed by the people closest to the affected content, the *area owners*, not funneled through a single gatekeeper. Area ownership is the repository's existing ownership map (a `CODEOWNERS` file, a team convention), not a new manifest field; Leji reuses it the way it reuses pull requests for approval. The primary owner is accountable for ensuring every area has one. Review asks more than "is this true?": why this belongs in the context layer, who will rely on it, what shows it holds, and when it should be revisited. A change that cannot answer those is a link or a note, not canonical context. The standing review question for any change set is *did this change alter the context?*; if yes, the context delta belongs in the same change set. 6. **Inclusion and removal.** Proposing is open; inclusion is not. Content belongs in the context layer only if it changes how future work is done: it sets a constraint, encodes a decision, defines an interface or ownership boundary, or stops a repeated mistake. Everything else is linked, not absorbed. The context layer **MUST** have a removal path as deliberate as its approval path: stale, superseded, and duplicated content is pruned in ordinary reviewed change sets, and pruning is part of each area owner's duty, not a separate cleanup project. A context layer that only ever grows is one that rots while passing review. Durable guidance **SHOULD** clear the proven-twice gate (per [content-categories.md](content-categories.md)): a one-off fix can be merged, but a norm becomes canonical only after it has held across at least two real tasks. Capture what is true, not what is hoped for. 7. **Changelog discipline.** At `indexed` conformance and above, every approved context layer change **MUST** append a machine-readable changelog entry per [machine-readable-surface.md](machine-readable-surface.md). 8. **Freshness.** Freshness is an **intent** mechanism: intent documents and agent profiles **SHOULD** carry review horizons (`freshness.reviewAfter` in index entries and profiles), and a **record** carries none (its date is its currency, per [content-categories.md](content-categories.md); a horizon declared on a record is a validation error). Tooling **SHOULD** report intent content whose horizon has passed, and **MUST NOT** silently treat stale content as current. A reader that loads context for a task **MUST** surface, in that task's output, any loaded item whose review horizon has passed, so the staleness is visible to the person rather than buried. Whether the *next expected record* in an operational series is overdue is a distinct notion (stream recency); 1.0 names it and defines no mechanism for it. A task's **required context** is the union of the boot profile's unconditional load set, the active agent profile's `requiredRead`, and the slice the Task routing algorithm selects for the task (its routed live decisions and routed governed intent documents, plus any record the task's paths select directly, per [machine-readable-surface.md](machine-readable-surface.md)). When a **required** item's horizon has expired, the reader **MUST** stop or ask rather than proceed on it; acting on context known to be past its review is the silent-staleness failure this rule exists to prevent. A stale item that is not required **MAY** be used with its staleness noted. At `governed` conformance, freshness horizons **MUST** be declared and checked (per the conformance checklist, report-only checking is acceptable); running the check in CI is **RECOMMENDED**. Review freshness (above) is distinct from *checkout currency*: whether the copy a reader holds matches the canonical repository. A reader establishes checkout currency from the version control system (git); the working tree is current only to the revision checked out, and tooling **MUST NOT** silently treat an unverified copy as current. A reader that reaches the context layer as plain file content, with no accessible git working tree or version metadata (file content uploaded or synced into another interface, without the repository), **MUST** treat checkout currency as unknown rather than current. 9. **Canonical content lives in the context layer.** Knowledge that governs how work is done **MUST NOT** exist only in a vendor configuration file, a chat thread, or an individual's notes. If it governs work, it belongs in the context layer, under review. ## Access boundary Leji defines no access-control mechanism of its own. **Access to a context layer is governed by the version control system (git) and the platform the repository lives on**: the repository host's permissions, and the filesystem or shared drive that exposes the working tree. The context layer is the unit of access. 10. A context layer **MAY** live in an access-controlled repository. Leji does not grant, check, or enforce that access; the version control system and its host do. 11. A conforming context layer **MUST NOT** require role-gated reading within itself. "Everyone reads" is scoped to a context layer's audience: everyone the version control system admits reads all of that context layer. 12. Content that needs a narrower audience (an executive, finance, security, or incident-response context) **MUST** live in a separate context layer with its own repository, manifest, owner, and review gate, permissioned by the version control system. Restricted context is a separate context layer, never a restricted region of a shared one. 13. Composing a restricted layer into another team's context is the [federation](distribution.md) case, with the additional rules for restricted mounts in [distribution.md](distribution.md). ## The maintenance model (non-normative) The context layer is maintained one delta at a time, riding work that is happening anyway: a task surfaces missing or wrong context; the fix travels in the same reviewed change set; the changelog records it. There's no separate documentation sprint, and there never needs to be one. Wrong context produces wrong output that someone feels the same day; that, plus review, plus CI on the mechanics, is the entire forcing system. That fast feedback catches *wrong* content quickly. Slow accumulation, content that is merely mediocre or redundant, is caught by the inclusion bar and the removal path above, applied by the people who own each area. This is curation, and Leji distributes it on purpose. A single curator looks like the safe way to hold quality and is the opposite: they become the slowest path in the system, changes queue behind them or route around them, and they hold less context than the area experts do. The context layer either stalls or forks. Every area owner pruning and gating their own slice is what keeps the whole context layer small and true without a chokepoint. The owner tends the health system; the circle tends the content. --- # Distribution Where the context layer lives relative to the work it describes. Three patterns; one rule throughout: the context layer is docs-only and **MUST NOT** introduce a build or runtime dependency into any consuming repository. ## Pattern 1: Monorepo (default) The context layer lives in the same repository as the code and infrastructure it describes, at the context root. This is the **RECOMMENDED** pattern wherever the team's work lives in one repository: code, infra, and context version together, and drift is structurally hard. ## Pattern 2: the docs-only submodule for multi-repo setups When work spans many repositories, the context layer lives in a dedicated context repository, and consuming repositories mount it as a git submodule. 1. The context repository is a normal git repository with its own `leji.json`, branch policies, and review gate. 2. Consuming repositories **MUST** mount it at a fixed path (**RECOMMENDED**: `context/`) and **MUST NOT** couple any build or runtime step to its presence: a missing or stale mount degrades knowledge, never the build. 3. Each consuming repository pins a specific version of the context layer. Pin updates **MUST** arrive as reviewable change sets (scripted or bot-raised pull requests), so context changes are visible, reviewable, and attributable per repository. 4. Tooling **SHOULD** report stale pins (how far each consuming repository is behind the context layer). Stale-pin reporting **MUST** precede any blocking enforcement: visibility first, gates later. The 1.0 reference SDK's pin reporting covers federation mounts (pattern 3); for this pattern's consumer-side pins it ships no check, so at `federated` this item is process-attested (see [conformance.md](conformance.md)); a team or its own tooling reports it until a reference check lands. ## Pattern 3: federation of sibling context layers Patterns 1 and 2 each have a single context layer: a monorepo owns one, and a multi-repo organization consumes one. Federation is the pattern for an organization where **more than one team already owns a context layer of its own**, and the goal is to make those context layers legible to each other without anyone surrendering control. The instinct is to merge them: one context repository, every team's knowledge pulled in. Resist it. A context layer stays current because the people who own it read it on every task and fix what's wrong in the same change set. Pull product's context into platform's repository and you have separated product's content from product's accountability; it rots while everyone assumes someone else now owns it. Centralizing knowledge recreates the bottleneck that put it in heads and threads to begin with. Federation composes the context layers instead of absorbing them. A team's context layer joins another team's graph as a **sibling**: mounted, referenced, and read, never copied. 1. A sibling context layer joins as a **pinned mount** declared in the host manifest's `federation.mounts`: the sibling's `name` and `owner`, its `source` repository locator, and a **`pin`** naming the full immutable commit id of the sibling revision the host reads. The pin is the mount's version of record, held in the manifest itself so pin updates arrive as reviewable change sets: a mount records *which version of another team's truth this repository was reading*, not a fork of it. A mount **MAY** declare `trackingRef`, a fully qualified branch or tag on the source that staleness and reachability are judged against; absent, the source's advertised default branch is used at check time and named in the report. 2. The sibling keeps everything that makes it alive: its own repository, owner, review gate, changelog, and conformance claim. The host **MUST NOT** copy sibling content into itself. Content separated from the team that owns it goes stale with nobody responsible, which is the exact failure federation exists to prevent. 3. **Mounted content materializes as a resolver-hydrated layer projection, never a committed copy.** A sibling is usually a layer embedded in a larger repository (pattern 1), so checking out the sibling wholesale would vendor a product to read its context. Instead, tooling extracts the **layer projection** at the pin, the deduplicated union of everything the sibling's own manifest makes readable (the root `leji.json`, the tree under the declared context root, the boot profile, the machine index and changelog files when present at the pin, the agent-profiles and decision-records trees when present, every agent profile named by the `agents` bindings, every category index file, and every governed path the pinned generated context index lists, wherever those live; the sibling's own manifest defines its projection, the host never curates it), into an ephemeral cache the host's version control ignores. The failure boundary follows the same line: a **referenced or schema-required** file absent at the pin (the boot profile, a category index, a bound agent profile, an indexed governed path) fails the projection with a stable code naming the declaring artifact and the missing path, while an absent directory or an absent machine artifact contributes nothing and fails nothing, whether its effective location was declared or defaulted; git cannot represent an empty directory, and a layer without a generated index simply has no content closure beyond its root tree. An availability-class projection failure (the pin's content is missing or malformed) leaves the mount unavailable on this machine and never fails ordinary host validation or the host's product build. A safety or internal projection failure (an escaping path, a malformed string, an exceeded limit) aborts hydration with a nonzero exit, and no partial projection is ever published. The pinned bytes are resolved from a **git object store** (a machine-local hint repository, the resolver-managed store, or a host submodule's object database), never from any working tree, and network access happens only as an explicit, consented step. A host repository **MAY** carry a submodule of the sibling for its own reasons; tooling treats it only as one more local object store, and a checkout is never readable mounted content. Committing sibling content into the host, including cache contents, does not conform. The docs-only rule from pattern 2 holds by construction: nothing in the host builds or runs against the projection. **A machine path that resolves to the repository root selects nothing.** A declared `machine.agentProfilesPath` or `machine.decisionRecordsPath` that resolves to the sibling's repository root contributes no directory selection to the projection: honoring it would vendor the entire sibling repository, which is the outcome the layer projection exists to avoid. Nothing referenced is lost, because profiles and decision records named individually, through the `agents` bindings or the pinned generated index, still travel; only the blanket root selection is dropped. **Projection limits.** A resolver **MUST** enforce four bounds, so that an independent implementation refuses the same inputs rather than each one choosing its own ceiling. A projection carries at most **65,536** entries, counted after deduplication. Its content totals at most **2 GiB** (2,147,483,648 bytes). No single projected path exceeds **4,096 bytes**, measured as the path's UTF-8 encoding rather than its characters, code points, or any runtime's native string units, which differ across implementations and would otherwise accept different paths. One whole-tree listing occupies at most **256 MiB** (268,435,456 bytes) in transport; this bounds the enumeration metadata a resolver reads in order to select, not the projected content the byte limit caps, and the two are deliberately different numbers because a large repository may hold a perfectly small valid projection. Exceeding any of the four is a **safety-class** projection failure. 4. **A mount that is not materialized degrades knowledge, never the build.** Validation separates three concerns. A manifest that lies is an **error**: duplicate mount names, a mount reusing the host's own `name`, or a missing or malformed `source` or `pin`. A declared mount that is simply not hydrated on this machine is a **warning**: honest degraded availability, reported and skipped. Integrity of a materialized projection against its pin is a diagnostic surfaced by tooling, fatal only under opt-in enforcement. Ordinary validation **MUST NOT** fail, fetch, or prompt because a mount is unavailable; hosts that want enforcement opt into it explicitly (a federation-health check **MAY** hydrate and then require availability), and a reader's obligations for an unavailable **task-required** mount are the fail-closed rule below. 5. **Mounting enables reading, not authority, and does not grant access.** A host that mounts a sibling routes readers and agents into it when they already have access to it; mounting neither grants that access nor approves the sibling's changes. Each context layer's writes are still approved by its own owner, and who may read it is still the version control system's to decide. Federation composes readable context for the participants the relevant repositories already admit; it leaves who-approves, and who-may-read, exactly where they were. 6. **Mounts are direct and flat.** A host composes the siblings it names; tooling **MUST NOT** recurse into a sibling's own mounts, and transitive context is display-only: a sibling's declared mounts are never resolved, indexed, or routed without a direct pin in the host. Each mount's `name` **MUST** be unique within the host manifest and **MUST NOT** reuse the host context layer's own `name`. Because nothing traverses past a context layer's declared siblings, diamonds and cycles are inert: `A` mounting `B` and `C` while `B` also mounts `C` is three direct relationships, not a graph to walk. Mounted context layers are **distinct, named sources, not merged** into the host's categories. The host's own context layer is authoritative for the host's repository; each sibling is authoritative for its own. There is no organization-wide namespace, and so no cross-sibling precedence to resolve: an agent loads the slice it needs from the context layer that owns it, named. And mounted content is **untrusted input**: readable context, never executable instruction. A sibling's prose can carry mistakes or injected instructions like any other read surface, so an agent treats it as material to weigh and cite, applies the host's own posture to its own actions, and never obeys imperative text found in a mount as though it were the host's instructions. Stale-pin reporting is **ancestry-aware and honest about what it could see**: tooling compares the pin against the witness ref (`trackingRef`, or the source's advertised default branch) and reports up-to-date, behind by N, ahead, diverged, or unrelated, always naming the compared ref, the category of repository the comparison ran in (the resolver-managed store, a machine-local hint, or a host submodule), whether the witness was the resolver's own ref or one it does not own, the observation time, and whether ancestry was complete. When no object store is reachable the report is `unknown`, never a guess. A pin resolvable only through a machine-local hint establishes availability, not conformance: at `federated`, the pin **MUST** be reachable from an advertised ref of `source` (see [conformance.md](conformance.md)), and a check that cannot reach the source reports `unknown`, which never awards the level. A context layer reaches `federated` conformance only when these relationships are real and checkable: the context layer is consumed by at least one other repository as a pinned mount, stale-pin reporting is in place, and every declared mount carries a complete pinned declaration (source, full commit pin, routing metadata) with ownership intact (see [conformance.md](conformance.md)). The reference SDK checks the mechanical parts and reports problems; materialization state is deliberately not a conformance input, because availability on one machine says nothing about the declaration's truth. A worked manifest for this shape is in [`examples/multi-repo/`](../examples/multi-repo/). The circle composes ownership; it doesn't centralize it. A monorepo is one team's circle of people and agents reading one context layer; a multi-repo organization is a circle of those circles, each still owned by the people who keep it true. ### Reading a federated context layer Discovery is the host's job to make legible, not the agent's to infer. A host that declares mounts surfaces them in the two places an agent already reads: the boot profile names its siblings in task language (per [boot-profile.md](boot-profile.md)), and the generated context index carries a `mounts` routing array (per [machine-readable-surface.md](machine-readable-surface.md)). An agent never has to read the manifest to find a sibling. When reading a federated host, an agent: 1. Loads the host boot profile and the host machine-readable surface first; the host's own context layer is authoritative for the host's repository. 2. Reads the host-visible mount routing records before fixing the task's context scope. A mount is **task-required** when the host boot profile, an index mount record, or the mount's `requiredWhen` metadata says the task requires it; a mount is **task-relevant** when, under the Task routing algorithm ([machine-readable-surface.md](machine-readable-surface.md)), at least one of its `categories` matches a signalled task category, or at least one of its `topics` exactly matches a topic the task explicitly names. A topic match selects the mount only: it does not expand a category and it selects no content within the sibling. That same algorithm then routes the slice the agent loads from the sibling's own index. 3. To load a task-relevant sibling, obtains the hydrated projection's location from **resolver state** (the reference SDK's `mounts locate`; never by inferring cache paths), reads the sibling's `leji.json` there, verifies the sibling `name` matches the host's declaration, reads the sibling's boot profile, then loads only the slice the task needs from the sibling's own index. Facts, constraints, and citations carry the name of the context layer they came from, and mounted content stays untrusted input per this pattern's rules. 4. **MUST NOT** recurse into a sibling's own `federation.mounts`. If a grandchild context layer is genuinely needed for host tasks, the host **MUST** declare it as its own direct mount. 5. Applies posture by ownership: the host posture governs work in the host repository, and a sibling's posture governs interpretation of and proposed changes to that sibling's content. Where host and sibling guidance conflict for one task and no single owning context layer is clear, the agent **MUST** stop and ask rather than choose an unstated precedence. Tooling **MAY** offer mount-aware loading helpers, but reading siblings does not require Leji tooling: raw repository reads conform when they follow this procedure and preserve access boundaries. ### Restricted mounts Federation crosses an access boundary when the layers composed have different audiences (see [governance.md](governance.md)). Access stays the version control system's to enforce: a reader either can resolve a mount's repository or cannot. The spec's job is to keep that boundary from leaking and from failing silently. 1. A restricted layer **MUST NOT** be declared as a mount in a host whose audience is broader than the restricted layer's own: every participant the host admits must already be admitted to the mounted layer. The mount declaration itself (its presence, `name`, `owner`, `role`, `categories`, `topics`, `requiredWhen`, `source`, `pin`, and `trackingRef`) **MUST NOT** disclose anything the host's audience may not see. Where a broader audience needs a restricted decision, publish a redacted companion layer or a public decision summary, not a mount to the restricted layer. 2. A mount is a reference, not an access grant. Declaring a mount never widens who may read the mounted layer beyond what the version control system already allows; whether a given reader can resolve it is decided there, not by the host manifest. 3. **Fail closed, never silently.** A reader that cannot resolve a **task-required** mount (as defined in [Reading a federated context layer](#reading-a-federated-context-layer)) **MUST** stop and report incomplete context. It **MUST NOT** proceed as though the inaccessible layer does not exist: an agent acting on partial context it cannot see is the failure this rule exists to prevent. The converse is also a failure: a reader that **can** resolve a task-required or task-relevant sibling but skips it anyway is acting on silently incomplete context and does not conform. What tooling can and cannot back here: a validator reports **local availability** (a declared mount with no hydrated projection here), and the routing algorithm decides **task-relevance** by category and topic overlap (the reference SDKs surface task-relevant mounts; see [machine-readable-surface.md](machine-readable-surface.md), Task routing). But **task-requiredness** turns on `requiredWhen`, which is free-text task conditions, and **runtime reachability** turns on the reader's own access at read time; both are the agent's to judge, not a tool's. The fail-closed MUST is therefore agent-attested: tooling surfaces what it can see, the agent enforces the stop. ## Notes (non-normative) The submodule pattern's bad reputation comes from code submodules with build coupling. A docs-only leaf has none of those failure modes: nothing compiles against it, nothing breaks when it lags, and the pin is just a recorded "which version of the truth was this repository working from", which is information, not risk. Federation looks like more moving parts than a merge, and is less. A merge is cheap once and expensive forever: every cross-team edit thereafter routes through whoever owns the central repository, and the parts no single team reads daily are the parts that rot. Sibling mounts keep each context layer small, owned, and read, and pay only the price of a pin update, which is a reviewable diff, not a meeting. --- # Conformance Partial adoption is by design. Four levels, each containing the previous; a team claims its level in the manifest (`conformance.claimedLevel`). Self-attestation only: there is no certification program. Conformance is evaluated against **the context layer as it is materialized where the check runs**, not against a canonical layer that a copy might represent. A copy reached without its repository is read in the degraded mode of [context-layer.md](context-layer.md), and degraded reading is never a path to canonical authority: such a copy does not verify, and the tooling says so rather than leaving the question open. Most checklist items are **machine-verified**: the reference tooling checks them against the layer and fails a claim they do not hold. Four outcomes are reported, and they are deliberately not interchangeable: - **`fail`**: the evidence was gathered and the requirement is not met. - **(process-attested)**, reported as **`manual`**: the item describes a team practice (a review gate, a CI job, an external consumer) that no tool can confirm from the repository alone, so the team stands behind it. Only items tagged **(process-attested)** below are ever reported this way. - **`unknown`**: a machine item whose evidence was unobtainable in this run, such as the federated pin-reachability check without access to the source, or append-only discipline with no git baseline to compare against. `unknown` never awards a level, and never refutes a claim that an evidence-bearing run could confirm. - **`not applicable`**: a conditional machine item that does not apply to this layer, such as the federated mount items on a layer that declares no mounts. It is not scored, and it is not evidence in either direction. The `verifiedLevel` the tooling reports is the highest level whose applicable **machine-verified** items all pass, **never above the level the layer claims**; `fail` and `unknown` both prevent an award, and items that are process-attested or not applicable are not scored. The cap on the claim is deliberate: verification answers whether the claim holds, not what the layer could claim, so a layer claiming `core` whose evidence would carry it to `governed` still reports `core`, and the way to raise the reported level is to raise the claim. `verifiedLevel` never asserts the process-attested items, so a passing `verifiedLevel` is necessary but not sufficient for a level that carries them. Each item below is machine-verified unless tagged **(process-attested)**. Two machine-verified items behave differently in a degraded copy, and the difference follows from what evidence each has. **Git presence** is answered: a copy that is not in a git repository does not meet the `core` requirement that the context layer lives in one, so the item is a `fail`. **Changelog append-only discipline** is not answered: the file may be entirely well-formed while the prior committed state needed to compare against is unreachable, so the item is `unknown` and the layer simply does not verify at `indexed` from that copy. Neither is reported `manual`, which is reserved for the tagged process-attested items. Separately, the freshness reader rule (surface stale loaded context, and stop or ask on an expired **required** item, per [governance.md](governance.md)) is reader-behavioral, not a conformance gate: the reference `leji route` stamps each routed document with its review horizon and expiry so an agent can apply it. Three items are verified today at less depth than their stated intent, and the gap is named here rather than left for a reader to discover. The boot-profile item is verified as presence at the declared path; whether it actually covers identity, loading, and posture is reported by the opt-in `--content` lint as warnings, not gated. The real-decision item is verified as schema-valid frontmatter on at least one resolved record; body substance (an actual decision, not a stub) likewise rides `--content`. The changelog item is the third: append-only discipline is checked against the file's state at `HEAD`, which catches a rewrite still in the working tree, the case a pre-commit hook exists for. In a continuous-integration checkout the working tree **is** `HEAD`, so a rewrite that arrives already committed is not visible to the check, and the review of the change set is what covers it. The item therefore verifies the working tree, not the history. The intent stated in each of the three items remains normative for what a conforming context layer carries; deepening the machine checks, and comparing the changelog against an explicit base revision, are on the reference-tooling roadmap. Verification of `federated` additionally requires at least one declared `federation.mounts` entry: a provider-only context layer (one that is consumed by other repositories but declares no mounts of its own) verifies at `governed`, and its federated standing rests on the process-attested consumption items. ## Level 1: `core` A context layer exists and both people and agents can work from it. - [ ] The context layer lives in a git repository, versioned with the work it describes (per [context-layer.md](context-layer.md), Requirements). - [ ] `leji.json` at the repository root, valid against the manifest schema. - [ ] A boot profile at the declared path, covering identity, loading, and posture. - [ ] At least `domain` or `system` mapped (via its index files) and populated with at least one resolved **intent** document (records alone carry no operating context), plus `decisions` with at least one **real** decision record: a record carrying a concrete `status` and an actual decision in its body, not an empty stub or placeholder. - [ ] A named primary owner. - [ ] Vendor entrypoint files, if present, redirect to the boot profile. ## Level 2: `indexed` The context layer is legible to tooling. - [ ] All of `core`. - [ ] A generated context index, current with the tree. - [ ] A machine-readable changelog; context layer changes append entries. ## Level 3: `governed` The forcing functions are mechanical, not goodwill. - [ ] All of `indexed`. - [ ] Context layer changes ride the repository's review gate; people approve. **(process-attested)** - [ ] Agent profiles (at least a core profile) valid against the profile schema. - [ ] CI validates the surface: manifest, index matches the tree, changelog discipline, profile frontmatter, declared paths resolve. **(process-attested)** - [ ] Freshness horizons are declared and checked (report-only is acceptable). ## Level 4: `federated` The context layer spans a multi-repo organization. - [ ] All of `governed`. - [ ] The context layer is consumed by at least one other repository as a pinned mount, with pin updates arriving as reviewable change sets. **(process-attested)** - [ ] Stale-pin reporting is in place: consumers can see how far their pins trail the witness ref. The reference SDK's ancestry-aware report covers declared federation mounts; consumption-side reporting beyond that is the team's. **(process-attested)** - [ ] Any sibling context layers are declared as complete pinned mounts per [distribution.md](distribution.md): a normalized `source` and a full commit `pin`, ownership intact. Materialization state on any one machine is not a conformance input. - [ ] Each declared mount's pin is reachable from an advertised ref of its `source` (the declared `trackingRef`, or the source's default branch). This check needs source access: without it the result is `unknown`, and `unknown` never awards the level. A pin resolvable only through a machine-local hint is availability, not conformance. - [ ] Each declared mount carries routing metadata: at least `categories`, plus `topics` or `requiredWhen`, so an agent can decide relevance without reading the sibling. - [ ] The boot profile surfaces every mounted sibling, and the generated index carries the `mounts` routing array, so an agent discovers and loads siblings without reading the manifest (per [boot-profile.md](boot-profile.md), [machine-readable-surface.md](machine-readable-surface.md)). ## Notes (non-normative) `core` is the minimum that makes a context layer real, `indexed` adds the generated surface tooling reads, `governed` is where the context layer stops depending on anyone's discipline, and `federated` is for organizations where more than one team already owns a context layer worth keeping whole. Most teams should reach `governed` and stop; `federated` exists for those organizations, not as a maturity badge. --- # Versioning Three things version independently: the specification, the schemas, and any implementing tooling. ## The specification 1. The spec carries a SemVer version (currently **1.0.0**). Breaking changes require a major version; every change is recorded in the repository changelog. 2. A context layer declares the spec line it targets in `leji.json` via the self-naming `leji` key (e.g. `"leji": "1.0"`), following the OpenAPI convention. The value is the spec **line** (`major.minor`), never the spec's patch version: a patch release (`1.0.0` to `1.0.1`) refines wording or tooling without moving the line, so the manifest stays `"1.0"` across every patch. Tooling **MUST** validate a context layer against the declared line, not the newest one. ## Preview lines A spec line **MAY** be designated **preview**. A preview line is revisable in place: it **MAY** change in ways that would otherwise be breaking, rather than being bumped to a new version, until it is frozen at general availability (GA). The "breaking changes require a major version" rule (item 1) and the "`$id` moves on an incompatible shape change" rule (item 3) apply from the GA freeze onward, not while a line is in preview. At GA the line is frozen and both rules take effect. A line that ships before general availability **MUST** declare that at its initial release. The 1.0 line is **frozen at the v1.3.0 reference-tooling release**. Within the line, schema changes are additive only and the `$id` stays on `v1.0`; any incompatible change ships as a new line, never in place. ## The schemas 3. Each schema carries a stable `$id` of the form `https://leji.org/schemas/v./.schema.json`. The `$id` line moves only when the schema's shape changes incompatibly. 4. Within a published line, schema changes **MUST** be additive (new optional fields). Field removals or semantic changes require a new line. 5. Machine-readable artifacts other than the manifest declare the schema line they were written against via `schemaVersion`; the manifest declares its target spec line via the self-naming `leji` key (item 2). ## Stability set The following are frozen within a spec line; tooling (including future commercial implementations) builds against them with no parallel schema: - the manifest shape and its fixed filename `leji.json`, - the category identifiers (`domain`, `system`, `practice`, `governance`, `decisions`), - the conformance level identifiers (`core`, `indexed`, `governed`, `federated`), - identifier and path normalization rules per [machine-readable-surface.md](machine-readable-surface.md), - the index entry, changelog entry, agent profile, and decision record shapes. ## Implementing tooling (non-normative) SDKs and CLIs version on their own SemVer and declare which spec lines they support. The reference SDKs in this repository are the `@leji-org/leji` npm package (packages/sdk), the `leji` PyPI package (packages/sdk-py), and the `leji` Go module (packages/sdk-go, a single static binary); they are behaviorally identical and tested against one shared fixture suite.