Skip to main content

24  The MANIFEST and the engram

This chapter lists every rule the manifest and the engram follow: each manifest section and its grammar, the declared name and the switches in the manifest’s frontmatter, every frontmatter key an engram can carry, the line syntax of the body and the schema engram. Reach for it when you write one of these files by hand, when a finding names a key you don’t recognize or when an agent’s edit surprised you. Chapter 4 tells the story of both files and why they look the way they do (Section 4.2); this chapter is the complete list, and it links back instead of repeating the why.

24.1 How the manifest is read

MANIFEST.md lives at a domain’s root as an engram of type: manifest with the permalink manifest, and a virtual domain keeps it in the database under that permalink. Crystalline reads its body line by line with a few strict rules, and knowing them saves you from a section that looks fine and counts for nothing.

H2 headings open sections
Only a ## heading starts a section. The heading text is matched with its whitespace runs collapsed and case ignored, so ## when TO use is the When to Use section.
What ends a section
The next H2 starts a new section and an H1 ends the current one: bullets after an H1 count for nothing until the next H2. An H3 or deeper heading does not end anything: bullets under a ### heading still count for the H2 above it.
Bullets
Only a line that starts with exactly - in the first column counts as a bullet of its section. An indented bullet, a * or + bullet and a numbered list item are ignored, and the text of a counted bullet is trimmed.
Fenced code
Lines inside a fence of three or more backticks or tildes are skipped, so an example heading or bullet inside a code block never counts.
Duplicate headings
The first section with a given heading wins. A later section with the same heading is ignored in full and draws a warning.
Findings on every edit
An agent’s edit of a manifest is never refused, because a manifest halfway through a rewrite may be wrong for one step; the receipt lists its findings instead, and Chapter 26 has what they carry and the codes themselves.

crystalline domain init, crystalline domain add --virtual and the add_domain tool all write the same scaffold. Its frontmatter has type: manifest, title set to the domain’s name, domain_name right after it with the same name, permalink: manifest, the tag manifest, status: stable and today’s recorded_at. Then come an H1 with the name and the three sections Scope, When to Use and Notes for Agents, each with placeholder bullets asking you to fill it in.

24.2 The sections

The parser reads four sections, and Notes for Agents is the fifth one worth knowing. The excerpt below shows the two sections the scaffold leaves out and one of the frontmatter switches, with lines kept short:

---
type: manifest
title: engineering
permalink: manifest
tags:
  - manifest
status: stable
recorded_at: 2026-03-02
generated_indexes: shared
---

## Provisioning

- skills: skills
- mcps: ../shared/mcps

## Tag Aliases

- k8s -> kubernetes
- runbook -> runbooks
## Scope
What belongs in the domain and what doesn’t, one bullet per statement. It’s required, and a Scope with no bullets is an error. It feeds routing only when When to Use is absent or has no bullets, and that fallback draws a warning of its own.
## When to Use
Says when an agent routes a task here, and it’s required: a When to Use with no bullets is an error like an empty Scope. Its bullets are the domain’s routing line, which shows up in the session-start briefing that crystalline prompt system prints, in the MCP server’s instructions and in list_domains with include_routing. A domain with neither When to Use nor Scope bullets gets a placeholder line asking you to add them.
There is no Routing section
A ## Routing heading is collected like any other H2 and means nothing. Routing comes from When to Use, with Scope as the fallback, and prompt.rules in the global configuration picks which domains a workspace sees (Chapter 23).
## Notes for Agents
The house rules for an agent working inside the domain, the folder layout first among them. Crystalline attaches no meaning to it; an agent reads it with read_engram when the routing line isn’t enough.
## Provisioning
The folders of tools the domain ships, one - <type>: <path> bullet for each of the four types skills, commands, agents and mcps. The types match in any case and the first bullet for a type wins; chapter 21 tells how a person allows or denies them per machine (Chapter 21).
Provisioning paths
A path is relative to the manifest, a trailing / is dropped and .. may climb out of the domain root. A path that starts with /, \ or ~, a bare ., an empty or . component and a component containing : or \ are rejected, and a rejected bullet is never provisioned.
Folders inside the root
A folder declared inside the root holds tools, so its files are neither indexed as engrams nor checked by crystalline verify.
## Tag Aliases
Old tag spellings folded into a canonical one, one - old -> canonical bullet each. The line splits at the first ->, both sides are trimmed and the search index folds the pair to lowercase, so a tag filter on either spelling finds engrams carrying the other.
Alias rules
Resolution is one hop: with a -> b and b -> c, a filter on a never reaches c, and the chain draws a warning. The old side is kept as written, because recording a spelling like multi_word is the point, while the canonical side should be lowercase with hyphens. The first mapping for an alias wins, and an exact repeat is dropped without comment.
Who writes aliases
crystalline tags merge <old> <into> records the fold in the manifest of every domain it rewrote, unless you pass --no-alias. crystalline tags rename records nothing, and an agent adds a bullet by hand only after you’ve agreed.
Any other H2
The parser collects such a section and checks its bullets and heading like any other, then ignores it. Review mode is not a manifest setting either: it lives in the domain’s registration (Chapter 25).

24.3 The declared name

domain_name
The domain’s name on every machine, as in domain_name: club. Links and crystalline:// addresses carry this name, whatever the folder, the repository or one machine’s registration is called, and a machine that registered the domain under another name still reaches it by this one; a Fluid address that uses it takes you to the domain’s page. It follows the same naming rule as any domain (Chapter 25), and Chapter 18 tells why it has to be unique and stable.
Who writes it
The scaffold carries it, and Crystalline adds it when a file or virtual domain that lacks it is registered. A domain defined by an environment variable is left alone, and so is a team domain: Crystalline never writes the key into a team domain’s manifest, so its owner adds it in the repository and shares it like any other change, and crystalline doctor names each team domain that still lacks it.
A value that can’t be a name
A number, a list or a text that breaks the naming rule is M008, reported by crystalline verify and on the receipt of an edit. Until it’s fixed Crystalline ignores the value, and the domain keeps the name it has.
Changing it
A rename sets it and rewrites the links that use the old name (Chapter 25). On a team domain the new value reaches the team with the next share, and each machine follows on its next sync unless its person chose a local name on purpose.

24.4 The switches

Six policies live in the manifest’s frontmatter instead of a section, since each is a single word: sharing, generated_indexes, read_only, reflection and the two loop triggers, reflect_trigger and evolve_trigger. Beside them sit three keys that nobody types, because Crystalline writes them itself: reflection_home, evolve_home and team. Their values are exact and case-sensitive, and a list or a mapping is never obeyed.

sharing
How a share of a team domain reaches the team, proposal or direct. A team domain shares through proposals by default; a manifest that says sharing: direct commits a share straight onto the branch, and chapter 17 tells the story (Chapter 17).
Direct sharing
A direct share lands the chosen files in one commit. It refuses while one of your own proposals is open, until that proposal is merged or withdrawn, and a share that names your own open proposal amends it. Another person’s open proposal doesn’t stop the commit, though the files only that proposal changed stay out of it, and no share ever goes into another person’s proposal. Whose proposal it is goes by GitHub login: under the default instance share identity everyone shares as the instance, so every proposal counts as your own.
Refused direct commits
A branch whose rules refuse your direct commit answers branch_protected and offers the same changes as a proposal: as_proposal: true on share_changes, crystalline origin share <domain> --proposal in a terminal and Share as a proposal in Fluid’s share dialog. The refusal is kept for the GitHub login the share went out on. Later shares from that login try the commit first and, when it’s refused, open a proposal on their own or go into that login’s open proposal, and the receipt says the share fell back. origin status and origin_status show the remembered refusal, and a direct commit that lands clears it. --direct (as_proposal: false) tries the commit only. While only another person’s proposal is open, a refused share offers no proposal and says to share again once that one is merged or closed. An account that can’t write the repository at all gets neither, and the share says so.
generated_indexes
Whether the generated index.md listings travel with a share, local (the default) or shared. With local each machine builds its own listings and a share neither carries them nor counts them as a change, which spares two proposals that touch one folder a conflict. Choose shared when something other than Crystalline reads the repository, so every folder stays browsable on the forge.
Listings already committed
Index files already in the repository stay when a domain moves to local, and no share proposes to delete them, so remove them by hand if you want them gone.
A value nobody recognizes
Any other value reads as the default, proposal, local or off, so a typo never turns a review off or publishes files nobody asked to publish; a misspelt read_only leaves the domain open, and M009 says so. crystalline verify reports the value as an error.
When a change takes effect
sharing and generated_indexes are read from the manifest at share time. On a team domain the changed manifest is an unshared change like any other and reaches the team with the next share; in a domain in review mode the change lands in your draft and applies once that draft lands.
Who changes them
Domain policies, in the domain’s settings in Fluid, shows the switches with their values, default and meaning. An instance admin or the owner of a private domain gets a select for each, where choosing direct or switching read_only on asks for a confirmation first; everyone else sees the values without a control. The same section lists domain_name with the value in effect, and an admin or owner gets a Change name button beside it that opens the rename. An agent reads and changes sharing, generated_indexes, reflection and the two triggers with configure and the domain, under the same rule; on an instance with auth.mcp off an anonymous agent may change them wherever it may write, which is never a domain in review mode. There an owner’s or admin’s change lands as a draft (Chapter 23). read_only keeps rules of its own, in its entry below.
read_only
Whether the domain is frozen, off (the default) or on. Only the domain’s owner or an instance admin switches it on, on every path through Crystalline, and an anonymous agent never does. An agent that acts as the owner or an admin, as a local one does, may switch it on when you ask it to, but no agent switches it off: over MCP every way to do that is refused, whoever is calling, and the answer tells the agent to ask the owner. The owner or an admin switches it off in Domain policies, in the domain’s settings, or with PATCH /domains/{domain}/manifest. While it’s on, every write into the domain through Crystalline is refused, whoever asks, and switching it back to off is the one change the domain takes. The switch counts from the moment the folder’s MANIFEST says so, never from a draft: in a domain in review mode a switch-on waits in the owner’s or admin’s draft until it lands, while the switch-off goes straight into the folder. Anyone who can reach the file can also edit the key outside Crystalline; that edit takes effect at the next write or sync, as for any MANIFEST key, and nothing records who made it. What it refuses and what keeps working is in Section 25.7.

The next two keys are for a domain’s Reflection (Chapter 12), and Domain policies has a row for each.

reflection
Whether the domain has a Reflection, off (the default) or on, switched by the domain’s owner or an instance admin. While it’s on, the reflections/ folder stays out of engram sync, the engram index, evolve, search_engrams and sharing, and the engram write tools refuse any path under it. Switching it on writes a first reflections/PACT.md with the pact’s five headings. It refuses while engrams live under reflections/ and names them, so you move them first, and a virtual domain refuses it, because a Reflection needs a domain kept as files. A plain clone of a team repository, whose MANIFEST says team: github, refuses it too: connect the domain and switch Reflection on at the team server.
reflection_home
The address of the instance where the domain’s Reflection lives. Nobody types it: switching reflection on in a domain with a GitHub origin writes this instance’s service.public_url here in the same change, and refuses when that setting is empty. On any other instance the Reflection tools refuse and name this address. A domain without a GitHub origin has no reflection_home, because its Reflection lives where the domain does.

The two loop triggers ask the instance to call a runner when a loop has work (Chapter 14). They’re changed like the other switches, in the manifest or on a trigger row of Domain policies, and only a domain manager or an admin sees the row’s delivery state (“last sent”, “last error”). Where the call goes is the instance’s setting (Chapter 23).

reflect_trigger
off (the default) or webhook. With webhook, the instance that is the domain’s Reflection home calls when the reflect queue has work: within a minute when a person waits for an answer, after 30 minutes without a further change when only pages need updating, and within a minute of a run releasing its lease while work is still due.
evolve_trigger
off (the default) or webhook. With webhook, the instance that runs the domain’s evolve loop calls when the mechanical findings reach automation.evolve_threshold, once each time the count crosses it. A weekly schedule in the runner stays the normal case.

The last two keys belong to a domain a team shares through GitHub.

evolve_home
The address of the instance that runs a team domain’s scheduled evolve loop (Chapter 14). Nobody types it: the first scheduled evolve run on an instance with a service.public_url writes that address here, and the run’s pull request carries it to the team. Every other copy names this address and refuses a scheduled run, while the sweep in a session works everywhere. On a shared database the home is asked first, then the host lock. To move it, remove the key at the instance that should run evolve (with configure and unset, or in the MANIFEST there), share that change and run evolve there once the team has it. A value that isn’t a server address reads as absent, and M013 says so; a domain only you keep ignores the key.
team
github or absent. It marks the repository as a team repository, and the first share from an instance connected to the domain writes it, named in the share’s preview and receipt. In a plain clone of such a repository, added as a local domain, switching Reflection on is refused and a Reflection that is already on doesn’t run; the engrams keep working. Connect the domain and switch Reflection on at the team server. Any other value reads as absent, and M010 says so.

24.5 The manifest in Fluid

The domain page shows the manifest as a page: its lead, When to Use with one line saying whether agents route by it or by Scope (or that nothing routes here until When to Use has a bullet) and the other sections in file order. Provisioning and Tag aliases sit in a rail beside it, each declaration as type: path and each mapping as old -> canonical, and a bullet that didn’t parse shows up with the reason.

Where When to Use or Scope is empty, the page explains the section in one line and shows a short example that saves as it stands, and the settings screen does the same for an empty Provisioning or Tag Aliases section, like - k8s -> kubernetes. An instance admin gets a button beside the example too, “Add Tag Aliases”, which opens the editor with the section seeded and writes nothing until you save. If the domain has no manifest at all, the page says so and an admin gets a “Create a MANIFEST” button. All of this is read-only on purpose: the document is the source and the editor is where a change goes, with the switches and the name as the exceptions, on the settings screen.

24.6 Engram frontmatter

The frontmatter is the YAML mapping between a --- line at the very top of the file and the next --- line. Without that closing line the file has no frontmatter at all, and a file with a byte order mark, a null byte or bytes that aren’t valid UTF-8 doesn’t parse. The six required keys are type, title, permalink, tags, status and recorded_at, and the write tools fill in the permalink, the status and the date, so an agent names the rest.

After a capture, edit_engram with set_frontmatter changes twelve keys one at a time: tags, sources, status, valid_from, valid_to, stale_after, source_date, resource, source_version, salience, verified and evolve_ack. tags and sources take the whole new list as values, and every other key takes one value. An edit that would change nothing writes nothing and answers unchanged: true. It refuses every other key, because identity, the capture date and the write provenance belong to the tools that maintain them.

24.6.1 Identity

type
What kind of engram this is, any non-empty string. The recommended values are engram, guide, decision, architecture, runbook and reference, plus manifest and schema for the two structural files; another value draws a note, never a refusal. A type: source engram holds a source’s verbatim text and gets four times the usual size budget.
title
The name the engram goes by, and what a wikilink resolves to. Title lookup ignores case, and two engrams with one title in a domain draw a warning.
permalink
The domain-relative half of the address: lowercase letters, digits, - and /, with folders as prefixes. It never carries the domain’s name, a crystalline:// scheme or a domain: prefix, and it can’t sit under the reserved assets/ folder. When the frontmatter names none, the file’s path stands in.
tags
A YAML list or a comma-separated string, never empty and matched without regard to case. Each tag should be lowercase letters, digits and hyphens without a hyphen at either end; another spelling still works and draws a warning.
status
Whether the knowledge holds, any string you like (Section 4.4). A value outside the recommended ones draws a note, and set_frontmatter changes a status but never removes it.
Status sets
stable and current are one class, and a filter on either finds both. deprecated, superseded, archived and legacy fade in ranking and are never filtered out, while draft, proposed, idea and poc are exempt from the sweep’s lifecycle rules. Only exact lowercase words count for all three, so Deprecated still ranks at full strength.
description
An optional one-line summary that feeds search snippets and the line beside the engram’s entry in the generated index.md.
resource
An optional URL or path, pointing at the main place the knowledge came from. Crystalline keeps it as written, and the ingestion record below depends on it. An engram an ingestion record fed that has no resource draws V111.
sources
An optional list of every location the engram was distilled from, the main one included. set_frontmatter keeps each entry exactly as written, because a search’s list filter matches it exactly. It drops a blank and a repeat, refuses an entry the index can’t hold (over 1024 bytes as JSON) by its position and removes the key when the list is empty.

24.6.2 Time

Every date key takes a plain ISO date, YYYY-MM-DD, with no time of day. A write that carries a timestamp, a number or any other shape in a date key is refused with the reason. A value already in a file that doesn’t parse is kept verbatim and flagged. Chapter 9 tells how searches read these keys (Chapter 9).

recorded_at
The day the knowledge was captured, required and set by the write tools; set_frontmatter won’t change it.
valid_from and valid_to
The validity window, both optional, and absence is the rule: no valid_from means the knowledge has always held and no valid_to means it holds until further notice. A null clears a bound. Crystalline drops a valid_to in the year 9000 or later and a valid_from in the year 1 or earlier instead of writing them, and valid_from must not come after valid_to.
source_date
The date the source material itself carries (a document’s publication date, say) when that differs from the day you captured it.
stale_after
The day at or after which the knowledge counts as stale and the sweep asks for a re-check. The older spelling review_after is still read, a stale_after write replaces it in place and clearing stale_after removes both.
temporal_confidence
Whether the dates were stated or worked out, explicit or inferred; any other value draws a warning.

24.6.3 Trust and provenance

generated
Who wrote the current revision and when, stamped on every write as one mapping. An agent’s write records the model beside who wrote it: generated: { by: ..., model: ..., at: ... }.
Actors
The actor in by is name/version for an agent, human:<account> for a person and process:<name> for a job, and a person’s entry never carries a model. When the client sends no actor and no identity.actor setting names one, an agent’s write records crystalline/mcp and a CLI write records process:crystalline-cli.
timestamp
The older write time, one RFC 3339 instant. Crystalline still reads it for recency where generated is missing, and new writes use generated instead.
verified
The verification trail, a list of { by, at } entries with an optional model; a single mapping counts as a one-entry list, and the sweep reads the newest entry that has an at (Chapter 10).
Recording a verification
set_frontmatter on verified with no value names you as the verifier: it replaces your own earlier entry, keeps everyone else’s and never clears the trail.
last_verified
The older verification key, a plain date. Crystalline falls back to it only when verified has no dated entry, and treats it as a check at midnight UTC by nobody in particular.

24.6.4 Keys with a meaning of their own

These keys aren’t part of the open format; Crystalline gives each one a meaning.

salience
A number from 0 to 10 that lifts the engram in search, bounded and never a filter; set_frontmatter refuses anything outside that range. The ceiling of the lift is the search.salience_weight setting (Chapter 23).
evolve_ack
The acknowledgments that silence sweep findings, a list of { rule, scope, note, by, at } entries. Through set_frontmatter the value is the rule and an optional note, V101 lineage citation, keep, and remove V101 takes one back (Chapter 10).
Acknowledgments by hand
The server fills in scope with the evidence the finding fired on. A hand-written entry without it matches whatever the rule finds, and an entry that names no rule is skipped.
schema
On an ordinary engram, it names the entity of the schema that governs it. Without this key, or when it names no schema, an engram is governed by the schema whose entity equals its type, and a name no schema declares draws a warning.
analyzes and analyzed_hash
On an engram that captured an attachment, the attachment’s assets/ path and its SHA-256 hash at the time. The sweep compares the hash with the file and asks for a re-read when they differ, and renaming the attachment rewrites analyzes along with it.
Unknown keys
Any other key survives every write and edit verbatim, after the known keys and in its original order, and a schema can still check it through settings.frontmatter.

24.7 The body

Everything below the frontmatter is markdown, and three kinds of line carry structure. Only lines outside fenced code count, and an inline code span never holds a link. Here’s what an observation, a relation and a prose link look like:

- [gotcha] Jobs older than 24h are dropped #payments (Mar 2026)
- depends_on [[Encom chargeback window]]
- "blocked by" [[clients:Encom contract renewal]]
The team calls the queue the sin bin, see [[Retry queue]].
Wrapped bullets
An observation or a relation runs over every line it wraps onto, indented or not, until a blank line, a heading, a fence, another list item, a block quote, a thematic break, a line that starts an HTML block (<) or a table row ends it.
Observation
A bullet that starts with - [ in the first column, shaped - [category] text #tag (context). The category is whatever sits inside the first brackets, free form and non-empty; chapter 4 lists the ones worth reusing. A ] directly followed by ( or [ closes a markdown link, so - [the docs](https://...) is no observation. A task box, [ ], [x] or [~], is no category either, so a task list stays a task list, and - [^1] text is a footnote, not an observation.
Tags and context in an observation
A trailing group in parentheses is taken off first as the observation’s context, then trailing #tag words are taken off as its tags, both from the end of the whole bullet. So a tag has to come before the context, a #tag after it stays in the text and so does a #tag that ends a line the bullet wraps past. After the #, a tag word takes letters, digits, -, _ or /.
Relation
A bullet in the first column shaped - rel_type [[Target]], which is an edge in the graph. The type is one word without spaces, or a phrase in double quotes; - see also [[X]] is no relation, only a prose link, and a type that starts with : or [ is none either. The first [[...]] is the target, a further link on the bullet counts as a prose link and anything after the closing brackets is ignored.
Relation types the checkers read
supersedes and superseded_by wire a retirement, summarizes and summarized_by a summary and its full text, and derived_from and split_into the two halves of a split. The sweep expects each pair wired both ways and raises a one-sided edge, and a superseded status without a superseded_by draws a warning.
Other relation types
Every other type is free, with one exception: V111 reads ingested_from, the link from an engram to the ingestion record that fed it. depends_on and relates_to are the conventional ones, the second for when nothing sharper fits.
Wikilink
[[Target]] anywhere in the body, resolved by permalink first and then by title in the engram’s own domain. A bare link never reaches another domain; when crystalline verify checks both domains in one run, its L001 names the domain that holds the title and the prefixed link to write. When an engram moves into another domain, its bare links gain the domain it left, so they keep finding the same engrams, and a prefix naming its new domain is dropped. The target’s page shows it as a backlink; on a line with several links, each one is counted once.
Cross-domain link
[[domain:Target]] resolves in the named domain only. The name to write is the declared domain_name (Chapter 18); a local name or a former name resolves on this machine only, and V110 flags it. The split happens at the first colon and only when the part before it has no spaces. A prefix that names a registered domain without the target leaves the link broken. Only when that part names no registered domain is the whole text tried as a permalink and then as a title at home, so [[Log: Weekly Notes]] still finds an engram with that title.
Display text
[[Target|text]] links to Target and shows text, and [[domain:Target|text]] does the same across domains. A move or a rename keeps the text.
Relative link
A Markdown link to another engram’s file by a relative path, as in [the pump](../pumps/p-12.md), with or without a # anchor, is an edge like a wikilink, and - part_of [Overview](../overview.md) is a relation. With a relative link the type is lowercase, like part_of, or a phrase in double quotes, so - See [Chapter 3](chapter-3.md) is a plain link; the wikilink form keeps its free type. When an engram moves inside its domain, the links to it and its own relative links are respelled so they keep working. A move into another domain turns them into wikilinks with the domain, because a relative path never reaches another domain.
Attachment reference
A markdown link or image whose target sits under the domain’s assets/ folder, and the sweep reads these references to find attachments nobody has analyzed yet.

24.8 Addresses and reserved names

The address and its one absolute form are explained in chapter 4 (Section 4.6); the entries below give every rule it follows.

crystalline://domain/permalink
The absolute address of one engram. A trailing /* anchors a whole folder for build_context, and a crystalline:// address you write in prose is checked by crystalline verify like a link.
Domain-relative identifiers
Anything without the scheme is relative to a domain given beside it, so retry-queue-gotcha together with the domain engineering is right, while engineering/retry-queue-gotcha names a folder called engineering.
Headings
An address names a whole engram, never a heading, and crystalline verify reports one with a # in it as broken. To open one section, use the engram’s web_url from a read or a write receipt and add # with the heading’s slug: lowercase, spaces as hyphens, letters, digits, hyphens and underscores kept and the rest dropped. A repeated heading gets -1, -2 and so on.
index.md and log.md
Both names are reserved at every folder level and never hold an engram. Crystalline regenerates index.md as the folder’s listing and never writes log.md, and a title that would slug to either is refused.
assets/
The folder attachments live in, and no permalink may point into it.

24.9 Schema engrams

A schema engram states what engrams of one type should contain, and validate_engrams checks them against it. It’s an engram of type: schema, and beside the usual engram keys its frontmatter carries the declaration in the Picoschema form:

type: schema
entity: runbook
version: 1
schema:
  service: string
  severity(enum): [low, high, critical]
  step(array): string
  owner?: Team
settings:
  validation: warn
  frontmatter:
    resource?: string
entity
The type the schema governs. An engram with that type, or with schema: naming it, is checked against it, and only schemas in the engram’s own domain count.
version
An integer that numbers the schema, kept as written.
schema
The field declarations for the body. A field with a lowercase type is an observation category of that name, so service: string asks for a - [service] ... line. A field with a capitalized type is a relation of that name, so owner?: Team names an optional - owner [[...]] relation.
Field names
A plain name is required and name? is optional. name(array) allows many lines, name(object) nests further declarations and name(enum) takes a YAML list of allowed values, one of which an observation’s text must match exactly.
Field types
string, integer, number, boolean, any or a capitalized entity name. A value of "string, what it holds" adds a description after the comma, and an unknown lowercase type counts as any while the definition draws an error.
settings.validation
How strict the check is: warn reports warnings, strict reports errors and off checks nothing. The default is warn, case doesn’t matter and any other value draws an error in the definition.
settings.frontmatter
Declarations for frontmatter keys, in the same syntax, including unknown ones. A finding names a nested key with a dot, as in review.owner.

infer_schema proposes a first schema from the engrams of a type that exist already. validate_engrams with drift lists the categories and relations in use that the schema doesn’t declare, and the declared ones nobody uses.

24.10 The ingestion record

The shipped skills add one convention on top of the format, and Crystalline reads only the links that tie a record to the engrams it fed. When an agent reads a source into a domain, a repository or a manual or a long document, it writes one record engram under sources/ with type: ingestion, titled after the source. The record carries resource for where the source lives, source_date where it has one, a verified entry and a source_version key holding a commit, a version, a revision or a content hash. It also lists the source’s stable addresses in sources, and there’s one record per source, which each run updates. Its body says what was extracted, which engrams received it as links and what was left out on purpose. Each engram the source fed carries its own resource, the main page or file it was distilled from (for a git source, the file at the ingested commit). It lists every location it came from in sources, the main one included, each in one spelling and without a version (for a git file, its address on the default branch). It links back with - ingested_from [[record]]. V111 raises one of them that lacks resource.

The next pass searches type: ingestion for the source’s resource and fetches only what changed since source_version. A filter on sources ($contains_any, up to 100 locations a call) finds the engrams of the changed locations, and the pass folds the change into them. It rewrites a renamed location in sources and points resource at the new path. It drops a location that’s gone from sources, and that alone is no reason to retire the engram. Then it moves source_version on and stamps verified. To Crystalline, source_version is an unknown key kept as written, and set_frontmatter moves it. Don’t mix the record up with a type: source engram, which holds a source’s own words (Chapter 8).

NoteIn brief

The manifest counts only H2 sections, matched without regard to case or spacing, and only - bullets in the first column, outside fences, with the first of two equal headings winning. Scope, When to Use, Notes for Agents, Provisioning and Tag Aliases are its sections, routing comes from When to Use with Scope as the fallback, and sharing, generated_indexes, read_only, reflection, reflect_trigger and evolve_trigger are its six frontmatter switches, each reading as its default whenever it says anything else, and Crystalline writes reflection_home, evolve_home and team itself. An engram needs type, title, permalink, tags, status and recorded_at, takes plain dates where it takes dates, treats an absent validity bound as unbounded and keeps every key it doesn’t know. Its body carries observations as - [category] text #tag (context), relations as - rel_type [[Target]] and links as [[Title]] or [[domain:Title]]. A type: schema engram declares in Picoschema what one type should contain, and the finding codes are in chapter 26.