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 useis 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 systemprints, in the MCP server’s instructions and inlist_domainswithinclude_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
## Routingheading is collected like any other H2 and means nothing. Routing comes from When to Use, with Scope as the fallback, andprompt.rulesin 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_engramwhen the routing line isn’t enough. ## Provisioning-
The folders of tools the domain ships, one
- <type>: <path>bullet for each of the four typesskills,commands,agentsandmcps. 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 -> canonicalbullet 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 -> bandb -> c, a filter onanever reachesc, and the chain draws a warning. The old side is kept as written, because recording a spelling likemulti_wordis 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 renamerecords 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 andcrystalline://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 doctornames 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 verifyand 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,
proposalordirect. A team domain shares through proposals by default; a manifest that sayssharing: directcommits 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
instanceshare 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_protectedand offers the same changes as a proposal:as_proposal: trueonshare_changes,crystalline origin share <domain> --proposalin 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 statusandorigin_statusshow 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.mdlistings travel with a share,local(the default) orshared. Withlocaleach 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. Choosesharedwhen 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,localoroff, so a typo never turns a review off or publishes files nobody asked to publish; a misspeltread_onlyleaves the domain open, and M009 says so.crystalline verifyreports the value as an error. - When a change takes effect
-
sharingandgenerated_indexesare 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
director switchingread_onlyon asks for a confirmation first; everyone else sees the values without a control. The same section listsdomain_namewith the value in effect, and an admin or owner gets a Change name button beside it that opens the rename. An agent reads and changessharing,generated_indexes,reflectionand the two triggers withconfigureand the domain, under the same rule; on an instance withauth.mcpoff 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_onlykeeps rules of its own, in its entry below. read_only-
Whether the domain is frozen,
off(the default) oron. 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 withPATCH /domains/{domain}/manifest. While it’s on, every write into the domain through Crystalline is refused, whoever asks, and switching it back tooffis 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) oron, switched by the domain’s owner or an instance admin. While it’s on, thereflections/folder stays out of engram sync, the engram index, evolve,search_engramsand sharing, and the engram write tools refuse any path under it. Switching it on writes a firstreflections/PACT.mdwith the pact’s five headings. It refuses while engrams live underreflections/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 saysteam: 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
reflectionon in a domain with a GitHub origin writes this instance’sservice.public_urlhere 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 noreflection_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) orwebhook. Withwebhook, 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) orwebhook. Withwebhook, the instance that runs the domain’s evolve loop calls when the mechanical findings reachautomation.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_urlwrites 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 (withconfigureandunset, 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-
githubor 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,runbookandreference, plusmanifestandschemafor the two structural files; another value draws a note, never a refusal. Atype: sourceengram 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, acrystalline://scheme or adomain:prefix, and it can’t sit under the reservedassets/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_frontmatterchanges a status but never removes it. - Status sets
-
stableandcurrentare one class, and a filter on either finds both.deprecated,superseded,archivedandlegacyfade in ranking and are never filtered out, whiledraft,proposed,ideaandpocare exempt from the sweep’s lifecycle rules. Only exact lowercase words count for all three, soDeprecatedstill 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
resourcedraws V111. sources-
An optional list of every location the engram was distilled from, the main one included.
set_frontmatterkeeps 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_frontmatterwon’t change it. valid_fromandvalid_to-
The validity window, both optional, and absence is the rule: no
valid_frommeans the knowledge has always held and novalid_tomeans it holds until further notice. A null clears a bound. Crystalline drops avalid_toin the year 9000 or later and avalid_fromin the year 1 or earlier instead of writing them, andvalid_frommust not come aftervalid_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_afteris still read, astale_afterwrite replaces it in place and clearingstale_afterremoves both. temporal_confidence-
Whether the dates were stated or worked out,
explicitorinferred; 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
byisname/versionfor an agent,human:<account>for a person andprocess:<name>for a job, and a person’s entry never carries a model. When the client sends no actor and noidentity.actorsetting names one, an agent’s write recordscrystalline/mcpand a CLI write recordsprocess:crystalline-cli. timestamp-
The older write time, one RFC 3339 instant. Crystalline still reads it for recency where
generatedis missing, and new writes usegeneratedinstead. verified-
The verification trail, a list of
{ by, at }entries with an optionalmodel; a single mapping counts as a one-entry list, and the sweep reads the newest entry that has anat(Chapter 10). - Recording a verification
-
set_frontmatteronverifiedwith 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
verifiedhas 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_frontmatterrefuses anything outside that range. The ceiling of the lift is thesearch.salience_weightsetting (Chapter 23). evolve_ack-
The acknowledgments that silence sweep findings, a list of
{ rule, scope, note, by, at }entries. Throughset_frontmatterthe value is the rule and an optional note,V101 lineage citation, keep, andremove V101takes one back (Chapter 10). - Acknowledgments by hand
-
The server fills in
scopewith 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
entityof the schema that governs it. Without this key, or when it names no schema, an engram is governed by the schema whoseentityequals itstype, and a name no schema declares draws a warning. analyzesandanalyzed_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 rewritesanalyzesalong 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] textis 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
#tagwords are taken off as its tags, both from the end of the whole bullet. So a tag has to come before the context, a#tagafter it stays in the text and so does a#tagthat 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
-
supersedesandsuperseded_bywire a retirement,summarizesandsummarized_bya summary and its full text, andderived_fromandsplit_intothe two halves of a split. The sweep expects each pair wired both ways and raises a one-sided edge, and asupersededstatus without asuperseded_bydraws 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_onandrelates_toare 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; whencrystalline verifychecks 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 declareddomain_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, likepart_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 forbuild_context, and acrystalline://address you write in prose is checked bycrystalline verifylike a link. - Domain-relative identifiers
-
Anything without the scheme is relative to a domain given beside it, so
retry-queue-gotchatogether with the domainengineeringis right, whileengineering/retry-queue-gotchanames a folder calledengineering. - Headings
-
An address names a whole engram, never a heading, and
crystalline verifyreports one with a#in it as broken. To open one section, use the engram’sweb_urlfrom 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,-2and so on. index.mdandlog.md-
Both names are reserved at every folder level and never hold an engram. Crystalline regenerates
index.mdas the folder’s listing and never writeslog.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?: stringentity-
The type the schema governs. An engram with that
type, or withschema: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: stringasks for a- [service] ...line. A field with a capitalized type is a relation of that name, soowner?: Teamnames 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 andname(enum)takes a YAML list of allowed values, one of which an observation’s text must match exactly. - Field types
-
string,integer,number,boolean,anyor a capitalized entity name. A value of"string, what it holds"adds a description after the comma, and an unknown lowercase type counts asanywhile the definition draws an error. settings.validation-
How strict the check is:
warnreports warnings,strictreports errors andoffchecks nothing. The default iswarn, 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).
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.