spec 1.0 · normative

Leji context manifest (leji.json)

The machine entrypoint of a Leji context layer. Lives at the repository root as leji.json.

Canonical $id:https://leji.org/schemas/v1.0/context-manifest.schema.json
Raw file:context-manifest.schema.json

Fields

#lejistringrequired

The Leji spec line this context layer targets, e.g. "1.0". The self-naming key follows the OpenAPI convention and identifies the file as a Leji manifest.

Pattern ^\d+\.\d+$

#namestringrequired

A short, stable identifier for this context layer, e.g. "acme-billing-context".

#descriptionstring

One-line summary of what this context layer covers.

#rootPathstringrequired

Context root, POSIX path relative to the repository root. Declares where the context layer lives; it does not re-base other paths: all paths in all Leji artifacts resolve from the repository root.

Pattern ^(?!/)(?!\./)(?!.*(^|/)\.\.(/|$))(?!.*\\).*$

#bootProfilePathstringrequired

Path to the boot profile, the agent-agnostic entrypoint every host and person starts from.

Pattern ^(?!/)(?!\./)(?!.*(^|/)\.\.(/|$))(?!.*\\).+\.md$

#categoriesobjectrequired

Logical category to index-file mapping. Keys are the five category identifiers; each maps to one or more curated index files that list the category's content.

Each key below takes the same shape:

indexesarray of stringsrequired

One or more repository-root-relative index files for this category. Each index file is curated markdown carrying a fenced `leji-index` block whose `- path: <dir-or-file>` lines select the content that belongs to this category. Content stays where it lives; the index file declares inclusion.

domainobject

Where domain content lives: business language and product semantics.

systemobject

Where system content lives: architecture and the invariants every change respects.

practiceobject

Where practice content lives: conventions and proven patterns.

governanceobject

Where governance content lives: agent guardrails and operating rules.

decisionsobject

Where decision records live: dated records of why things are the way they are.

#machineobject

Locations of the machine-readable artifacts: index, changelog, profiles, and decision records.

indexPathstring

Path to the generated context index.

changelogPathstring

Path to the machine-readable context changelog.

agentProfilesPathstring

Directory holding agent profile documents.

decisionRecordsPathstring

Directory holding decision records.

#agentsmap

Role to agent-profile binding. Keys are role identifiers (e.g. "thought-partner", "reviewer"); values are paths to agent profile documents. Protocols engage roles; this map decides who fills them.

#actorsmap

Optional registry of actors that can fill roles. Keys are stable actor identifiers. An actor declares the roles it is eligible for and a command template per role. Use it when a role has more than one eligible actor, or when one actor needs a different invocation depending on which role it is filling; either alone is reason enough. A role whose single actor needs only one command is served by the agent profile's own host and invocation.

#ownersobjectrequired

Who is accountable for the context layer's health. Owners answer for the context layer staying current and pruned; they do not author or curate it alone (governance.md).

Each key below takes the same shape:

namestringrequired

The person's name.

contactstring

How to reach them, e.g. an email address.

primaryobjectrequired

The person accountable for the context layer's currency. Ownerless context layers rot.

continuityobject

Optional. A different person who carries the same accountability when the primary is unavailable or steps away; assisted adoptions should name one before outside help leaves. Naming the primary again provides no continuity, and an agent cannot fill it: owners are accountable people.

#conformanceobject

The conformance level this context layer claims, checkable by tooling.

claimedLevel"core" · "indexed" · "governed" · "federated"

The conformance level claimed: core, indexed, governed, or federated.

claimedAtstring

ISO 8601 date the claim was last asserted.

#federationobject

Sibling layers mounted into this repository (spec: distribution.md pattern 3). The circle composes ownership; it doesn't centralize it.

mountsarray of objects

Each mount is a sibling context layer read at a pinned version through a resolver-hydrated layer projection, keeping its own repository, ownership, and review gate. Materialized content lives in the gitignored resolver cache, never committed here.

namestringrequired

The sibling layer's name; must match the `name` in the sibling's own manifest.

sourcestringrequired

Normalized repository locator of the sibling layer: an https://, ssh://, or SCP-style remote URL. The resolution root and the pin's namespace. Local filesystem paths are valid only in machine-local hints, never here.

pinstringrequired

Full immutable commit id (SHA-1 or SHA-256) of the sibling revision this host reads. Manifest-held desired state: pin updates are deliberate, reviewed curation.

trackingRefstring

Optional fully qualified witness ref on the source (refs/heads/* or refs/tags/*) that stale-pin reporting and federated reachability compare against. Absent, the source's advertised default branch is used and recorded in command output.

ownerobjectrequired

The owner of the sibling layer. Ownership stays with the sibling's team; the host never absorbs its content.

rolestring

What this sibling layer carries, in prose, e.g. "product-side context". For humans; routing uses the structured fields below.

categoriesarray of "domain" · "system" · "practice" · "governance" · "decisions"

The content-category scopes this sibling can answer for. Routing metadata only; the sibling's content stays in the sibling. Required for federated conformance.

topicsarray of strings

Short host-visible topic labels used to route a task to this sibling, e.g. "billing", "checkout". Routing metadata only.

requiredWhenarray of strings

Task conditions under which a reader must resolve this mount, or stop and report incomplete context if it cannot (fail closed). Host-visible; must not disclose anything the host's audience may not see.

#vendorAdaptersarray of strings

Vendor entrypoint files present in this repository; each must redirect to the boot profile.

#viewerobject

Presentation preferences read by `leji viewer`. Non-normative convenience configuration; presentation itself is out of normative scope.

portinteger

Preferred local-preview port for `leji viewer serve`. The --port flag overrides; the default is 5354 (LEJI on a phone keypad).

logostring

Logo image for the viewer, as a path under the context root (e.g. "assets/logo.svg"). Defaults to the Leji mark.

titlestring

Display title for the viewer (sidebar header and page title). Defaults to the context layer name.

agentsLabelstring

Sidebar label for the agent-profiles group (emoji welcome). Defaults to "🤖 Agents". The group lists the layer's agent profiles and is ordered by viewer.groupOrder like any other group.

faviconstring

Favicon for the viewer, as a path under the context root (e.g. "assets/icon.svg"). Defaults to the Leji mark.

homepagestring

The viewer's landing page, as a path relative to the context root (e.g. "README.md"). Defaults to the seeded overview.md.

pinsarray of items

Pages pinned at the top of the sidebar: repository-root-relative markdown paths, or {path, label} for a curated label. Pinning the boot profile replaces its default line.

groupOrderarray of strings

Curated sidebar group sequence, by exact group label (the index file's H1). Listed groups come first in this order; unlisted groups follow in derived order.

themeobject

Viewer theme overrides.

primarystring

Primary/accent color as a hex CSS color (e.g. "#009F71"). Drives links, the active state, and diagram accents.

mermaidboolean

Render fenced ```mermaid code blocks as diagrams in the viewer. Defaults to true.

poweredByboolean

Show the small "Powered by Leji" mark in the viewer's corner. Defaults to true; set false to remove it.

categoryEmojisobject

Override the default emoji shown beside each category in the generated layer map. (Sidebar groups are labeled by each index file's own H1.)

Schema and example

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://leji.org/schemas/v1.0/context-manifest.schema.json",
  "title": "Leji context manifest (leji.json)",
  "description": "The machine entrypoint of a Leji context layer. Lives at the repository root as leji.json.",
  "type": "object",
  "required": [
    "leji",
    "name",
    "rootPath",
    "bootProfilePath",
    "categories",
    "owners"
  ],
  "additionalProperties": false,
  "properties": {
    "$schema": {
      "description": "Optional pointer to this schema, for editor validation and tooling.",
      "type": "string"
    },
    "leji": {
      "type": "string",
      "description": "The Leji spec line this context layer targets, e.g. \"1.0\". The self-naming key follows the OpenAPI convention and identifies the file as a Leji manifest.",
      "pattern": "^\\d+\\.\\d+$"
    },
    "name": {
      "description": "A short, stable identifier for this context layer, e.g. \"acme-billing-context\".",
      "type": "string",
      "minLength": 1
    },
    "description": {
      "description": "One-line summary of what this context layer covers.",
      "type": "string"
    },
    "rootPath": {
      "type": "string",
      "description": "Context root, POSIX path relative to the repository root. Declares where the context layer lives; it does not re-base other paths: all paths in all Leji artifacts resolve from the repository root.",
      "pattern": "^(?!/)(?!\\./)(?!.*(^|/)\\.\\.(/|$))(?!.*\\\\).*$"
    },
    "bootProfilePath": {
      "description": "Path to the boot profile, the agent-agnostic entrypoint every host and person starts from.",
      "type": "string",
      "pattern": "^(?!/)(?!\\./)(?!.*(^|/)\\.\\.(/|$))(?!.*\\\\).+\\.md$"
    },
    "categories": {
      "type": "object",
      "description": "Logical category to index-file mapping. Keys are the five category identifiers; each maps to one or more curated index files that list the category's content.",
      "additionalProperties": false,
      "minProperties": 1,
      "properties": {
        "domain": {
          "description": "Where domain content lives: business language and product semantics.",
          "$ref": "#/$defs/categoryMapping"
        },
        "system": {
          "description": "Where system content lives: architecture and the invariants every change respects.",
          "$ref": "#/$defs/categoryMapping"
        },
        "practice": {
          "description": "Where practice content lives: conventions and proven patterns.",
          "$ref": "#/$defs/categoryMapping"
        },
        "governance": {
          "description": "Where governance content lives: agent guardrails and operating rules.",
          "$ref": "#/$defs/categoryMapping"
        },
        "decisions": {
          "description": "Where decision records live: dated records of why things are the way they are.",
          "$ref": "#/$defs/categoryMapping"
        }
      }
    },
    "machine": {
      "description": "Locations of the machine-readable artifacts: index, changelog, profiles, and decision records.",
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "indexPath": {
          "description": "Path to the generated context index.",
          "$ref": "#/$defs/relPath"
        },
        "changelogPath": {
          "description": "Path to the machine-readable context changelog.",
          "$ref": "#/$defs/relPath"
        },
        "agentProfilesPath": {
          "description": "Directory holding agent profile documents.",
          "$ref": "#/$defs/relPath"
        },
        "decisionRecordsPath": {
          "description": "Directory holding decision records.",
          "$ref": "#/$defs/relPath"
        }
      }
    },
    "agents": {
      "type": "object",
      "description": "Role to agent-profile binding. Keys are role identifiers (e.g. \"thought-partner\", \"reviewer\"); values are paths to agent profile documents. Protocols engage roles; this map decides who fills them.",
      "additionalProperties": false,
      "patternProperties": {
        "^[a-z0-9]+(-[a-z0-9]+)*$": {
          "$ref": "#/$defs/relPath"
        }
      }
    },
    "actors": {
      "type": "object",
      "description": "Optional registry of actors that can fill roles. Keys are stable actor identifiers. An actor declares the roles it is eligible for and a command template per role. Use it when a role has more than one eligible actor, or when one actor needs a different invocation depending on which role it is filling; either alone is reason enough. A role whose single actor needs only one command is served by the agent profile's own host and invocation.",
      "additionalProperties": false,
      "patternProperties": {
        "^[a-z0-9]+(-[a-z0-9]+)*$": {
          "$ref": "#/$defs/actor"
        }
      }
    },
    "owners": {
      "type": "object",
      "description": "Who is accountable for the context layer's health. Owners answer for the context layer staying current and pruned; they do not author or curate it alone (governance.md).",
      "required": [
        "primary"
      ],
      "additionalProperties": false,
      "properties": {
        "primary": {
          "$ref": "#/$defs/owner",
          "description": "The person accountable for the context layer's currency. Ownerless context layers rot."
        },
        "continuity": {
          "$ref": "#/$defs/owner",
          "description": "Optional. A different person who carries the same accountability when the primary is unavailable or steps away; assisted adoptions should name one before outside help leaves. Naming the primary again provides no continuity, and an agent cannot fill it: owners are accountable people."
        }
      }
    },
    "conformance": {
      "description": "The conformance level this context layer claims, checkable by tooling.",
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "claimedLevel": {
          "description": "The conformance level claimed: core, indexed, governed, or federated.",
          "enum": [
            "core",
            "indexed",
            "governed",
            "federated"
          ]
        },
        "claimedAt": {
          "description": "ISO 8601 date the claim was last asserted.",
          "$ref": "#/$defs/isoDate"
        }
      }
    },
    "federation": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "mounts": {
          "type": "array",
          "items": {
            "type": "object",
            "required": [
              "name",
              "source",
              "pin",
              "owner"
            ],
            "additionalProperties": false,
            "properties": {
              "name": {
                "type": "string",
                "minLength": 1,
                "description": "The sibling layer's name; must match the `name` in the sibling's own manifest."
              },
              "source": {
                "type": "string",
                "minLength": 1,
                "description": "Normalized repository locator of the sibling layer: an https://, ssh://, or SCP-style remote URL. The resolution root and the pin's namespace. Local filesystem paths are valid only in machine-local hints, never here."
              },
              "pin": {
                "type": "string",
                "pattern": "^([0-9a-f]{40}|[0-9a-f]{64})$",
                "description": "Full immutable commit id (SHA-1 or SHA-256) of the sibling revision this host reads. Manifest-held desired state: pin updates are deliberate, reviewed curation."
              },
              "trackingRef": {
                "type": "string",
                "pattern": "^refs/(heads|tags)/.+$",
                "description": "Optional fully qualified witness ref on the source (refs/heads/* or refs/tags/*) that stale-pin reporting and federated reachability compare against. Absent, the source's advertised default branch is used and recorded in command output."
              },
              "owner": {
                "$ref": "#/$defs/owner",
                "description": "The owner of the sibling layer. Ownership stays with the sibling's team; the host never absorbs its content."
              },
              "role": {
                "type": "string",
                "description": "What this sibling layer carries, in prose, e.g. \"product-side context\". For humans; routing uses the structured fields below."
              },
              "categories": {
                "type": "array",
                "items": {
                  "enum": [
                    "domain",
                    "system",
                    "practice",
                    "governance",
                    "decisions"
                  ]
                },
                "minItems": 1,
                "description": "The content-category scopes this sibling can answer for. Routing metadata only; the sibling's content stays in the sibling. Required for federated conformance."
              },
              "topics": {
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1
                },
                "description": "Short host-visible topic labels used to route a task to this sibling, e.g. \"billing\", \"checkout\". Routing metadata only."
              },
              "requiredWhen": {
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1
                },
                "description": "Task conditions under which a reader must resolve this mount, or stop and report incomplete context if it cannot (fail closed). Host-visible; must not disclose anything the host's audience may not see."
              }
            }
          },
          "description": "Each mount is a sibling context layer read at a pinned version through a resolver-hydrated layer projection, keeping its own repository, ownership, and review gate. Materialized content lives in the gitignored resolver cache, never committed here."
        }
      },
      "description": "Sibling layers mounted into this repository (spec: distribution.md pattern 3). The circle composes ownership; it doesn't centralize it."
    },
    "vendorAdapters": {
      "type": "array",
      "description": "Vendor entrypoint files present in this repository; each must redirect to the boot profile.",
      "items": {
        "$ref": "#/$defs/relPath"
      }
    },
    "viewer": {
      "type": "object",
      "description": "Presentation preferences read by `leji viewer`. Non-normative convenience configuration; presentation itself is out of normative scope.",
      "additionalProperties": false,
      "properties": {
        "port": {
          "type": "integer",
          "minimum": 1,
          "maximum": 65535,
          "description": "Preferred local-preview port for `leji viewer serve`. The --port flag overrides; the default is 5354 (LEJI on a phone keypad)."
        },
        "logo": {
          "type": "string",
          "description": "Logo image for the viewer, as a path under the context root (e.g. \"assets/logo.svg\"). Defaults to the Leji mark."
        },
        "title": {
          "type": "string",
          "description": "Display title for the viewer (sidebar header and page title). Defaults to the context layer name."
        },
        "agentsLabel": {
          "type": "string",
          "description": "Sidebar label for the agent-profiles group (emoji welcome). Defaults to \"🤖 Agents\". The group lists the layer's agent profiles and is ordered by viewer.groupOrder like any other group."
        },
        "favicon": {
          "type": "string",
          "description": "Favicon for the viewer, as a path under the context root (e.g. \"assets/icon.svg\"). Defaults to the Leji mark."
        },
        "homepage": {
          "type": "string",
          "description": "The viewer's landing page, as a path relative to the context root (e.g. \"README.md\"). Defaults to the seeded overview.md."
        },
        "pins": {
          "type": "array",
          "items": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "path"
                ],
                "properties": {
                  "path": {
                    "type": "string",
                    "description": "Repository-root-relative markdown path to pin."
                  },
                  "label": {
                    "type": "string",
                    "description": "Curated sidebar label for the pin (emoji welcome). Derived from the file when omitted."
                  }
                }
              }
            ]
          },
          "description": "Pages pinned at the top of the sidebar: repository-root-relative markdown paths, or {path, label} for a curated label. Pinning the boot profile replaces its default line."
        },
        "groupOrder": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Curated sidebar group sequence, by exact group label (the index file's H1). Listed groups come first in this order; unlisted groups follow in derived order."
        },
        "theme": {
          "type": "object",
          "additionalProperties": false,
          "description": "Viewer theme overrides.",
          "properties": {
            "primary": {
              "type": "string",
              "description": "Primary/accent color as a hex CSS color (e.g. \"#009F71\"). Drives links, the active state, and diagram accents."
            }
          }
        },
        "mermaid": {
          "type": "boolean",
          "description": "Render fenced ```mermaid code blocks as diagrams in the viewer. Defaults to true."
        },
        "poweredBy": {
          "type": "boolean",
          "description": "Show the small \"Powered by Leji\" mark in the viewer's corner. Defaults to true; set false to remove it."
        },
        "categoryEmojis": {
          "type": "object",
          "additionalProperties": {
            "type": "string"
          },
          "propertyNames": {
            "enum": [
              "domain",
              "system",
              "practice",
              "governance",
              "decisions"
            ]
          },
          "description": "Override the default emoji shown beside each category in the generated layer map. (Sidebar groups are labeled by each index file's own H1.)"
        }
      }
    }
  },
  "$defs": {
    "relPath": {
      "type": "string",
      "pattern": "^(?!/)(?!\\./)(?!.*(^|/)\\.\\.(/|$))(?!.*\\\\).+$"
    },
    "isoDate": {
      "type": "string",
      "pattern": "^\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])([T ].*)?$"
    },
    "owner": {
      "type": "object",
      "required": [
        "name"
      ],
      "additionalProperties": false,
      "properties": {
        "name": {
          "description": "The person's name.",
          "type": "string",
          "minLength": 1
        },
        "contact": {
          "description": "How to reach them, e.g. an email address.",
          "type": "string"
        }
      }
    },
    "categoryMapping": {
      "type": "object",
      "required": [
        "indexes"
      ],
      "additionalProperties": false,
      "properties": {
        "indexes": {
          "description": "One or more repository-root-relative index files for this category. Each index file is curated markdown carrying a fenced `leji-index` block whose `- path: <dir-or-file>` lines select the content that belongs to this category. Content stays where it lives; the index file declares inclusion.",
          "type": "array",
          "minItems": 1,
          "items": {
            "type": "string",
            "pattern": "^(?!/)(?!\\./)(?!.*(^|/)\\.\\.(/|$))(?!.*\\\\).+\\.md$"
          }
        }
      }
    },
    "actor": {
      "type": "object",
      "required": [
        "roles",
        "commands"
      ],
      "additionalProperties": false,
      "properties": {
        "roles": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": {
            "$ref": "#/$defs/roleId"
          },
          "description": "Role identifiers this actor is eligible to fill. Every entry must have a matching key in commands."
        },
        "commands": {
          "type": "object",
          "minProperties": 1,
          "propertyNames": {
            "$ref": "#/$defs/roleId"
          },
          "additionalProperties": {
            "$ref": "#/$defs/actorCommand"
          },
          "description": "Command templates keyed by role identifier. The key set must equal roles. Keying by role is the point: one actor can need different invocations for different roles."
        }
      }
    },
    "actorCommand": {
      "type": "string",
      "minLength": 1,
      "pattern": "<prompt>",
      "description": "Command template used to engage this actor in the keyed role. Must carry the <prompt> placeholder, standing as its own unquoted shell word; the pattern verifies the substitution site exists, placement is the spec's rule."
    },
    "roleId": {
      "type": "string",
      "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$",
      "description": "A role identifier, lowercase and hyphen-separated, as used by the agents map."
    }
  }
}

A conforming manifest at a repository root.

{
  "$schema": "https://leji.org/schemas/v1.0/context-manifest.schema.json",
  "leji": "1.0",
  "name": "acme-billing-context",
  "description": "Shared context layer for the Acme billing platform monorepo.",
  "rootPath": "docs/",
  "bootProfilePath": "docs/boot-profile.md",
  "categories": {
    "domain": {
      "indexes": [
        "docs/context/domain.md"
      ]
    },
    "system": {
      "indexes": [
        "docs/context/system.md"
      ]
    },
    "decisions": {
      "indexes": [
        "docs/context/decisions.md"
      ]
    }
  },
  "machine": {
    "indexPath": "docs/context-index.json",
    "changelogPath": "docs/context-changelog.json",
    "agentProfilesPath": "docs/agents/",
    "decisionRecordsPath": "docs/decisions/"
  },
  "agents": {
    "thought-partner": "docs/agents/thought-partner.md"
  },
  "owners": {
    "primary": {
      "name": "Jo Lee",
      "contact": "jo@acme.example"
    }
  },
  "conformance": {
    "claimedLevel": "indexed",
    "claimedAt": "2026-06-10"
  }
}

← All schemas