{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "TypeDefinition",
"type": "object",
"properties": {
"boundaries": {
"type": "array",
"default": [],
"items": {
"type": "string"
}
},
"constraints": {
"description": "Declared keep-health constraints (the constraint vocabulary —\nsee [`ConstraintDef`]). Empty default: a schema declaring no\nconstraints behaves byte-identically to before the vocabulary\nexisted.",
"type": "array",
"items": {
"$ref": "#/$defs/ConstraintDef"
}
},
"description": {
"type": "string"
},
"due": {
"description": "The type's declared due axis (see [`DueAxis`]) — absent for\ntypes without deadline semantics; a schema without any `due:`\ndeclaration behaves byte-identically to before the axis\nexisted.",
"anyOf": [
{
"$ref": "#/$defs/DueAxis"
},
{
"type": "null"
}
]
},
"edge_weight_overrides": {
"type": "object",
"additionalProperties": {
"type": "number",
"format": "float"
},
"default": {}
},
"exemplar": {
"description": "One canonical, ENGINE-VALIDATED exemplar entity for this type\n(agent-trust plan 09) — the few-shot material an authoring\nagent actually learns from. Validated against this very type\nthrough the real create path (`dry_run`) at schema\ninstall/seal time: a package whose exemplar does not conform\nrefuses with a typed error naming the type and the defect —\nthere is no warn-and-carry mode, so an exemplar can never\ndrift into teaching the wrong shape. Served at\n`verbosity: full` only (the lite skeleton stays unchanged).\nOptional per type; the built-in reference schemas are complete.",
"anyOf": [
{
"$ref": "#/$defs/Exemplar"
},
{
"type": "null"
}
]
},
"health_required_fields": {
"type": "array",
"items": {
"type": "string"
}
},
"hierarchy_relationship": {
"type": "string"
},
"leaf": {
"description": "Terminal-by-construction marker: entities of this type are\nleaves — they carry no edges BY DESIGN, so health's orphan\naxis exempts their edge-less entities and reports them as a\nseparate leaf population instead (visible, never vanished).\nLeaf means \"no edges required\", not \"edges forbidden\": a\nleaf-typed entity WITH edges stays legal, and every other\nhealth axis, search, and traversal treats leaf entities\nexactly like any other. Declarative per-type flag (the sixth\ndeclarative form the agent-toolbox constraint vocabulary\nanticipated), served at both schema verbosity levels.",
"type": "boolean"
},
"metadata_fields": {
"type": "array",
"items": {
"$ref": "#/$defs/MetadataFieldDef"
}
},
"name": {
"type": "string"
},
"no_self_loop_relationships": {
"description": "Rel-types on which `memstead_relate` refuses a SELF-LOOP\n(from == to) when this type is the source. That refusal is this\nfield's ONLY effect — it propagates nothing, implies no\nevidence obligation (real impact propagation is the\n`status_propagation` constraint). Renamed from the misleading\n`propagating_relationships` (agent-trust plan 06): the old key\nrefuses at authoring/install load with a typed error naming\nthis one; sealed content (built-ins, installed refs) loads\nwith the old key translated.",
"type": "array",
"items": {
"type": "string"
}
},
"required_outgoing": {
"description": "Outgoing-edge invariants the schema asserts for this type.\nEach entry names a list of relationship names plus a cardinality\nconstraint. The engine evaluates these on every `memstead_create` /\n`memstead_update` (post-application of inline `relations:` / patches)\nand surfaces unsatisfied blocks as a single\n`MISSING_REQUIRED_OUTGOING` warning per entity. Tier-2 (warn,\nnever block). Empty default — types without `required_outgoing`\nkeep current behaviour.",
"type": "array",
"items": {
"$ref": "#/$defs/RequiredOutgoing"
}
},
"sections": {
"type": "array",
"items": {
"$ref": "#/$defs/SectionDef"
}
},
"staleness_threshold_days": {
"type": "integer",
"format": "uint32",
"minimum": 0
},
"system_message": {
"type": [
"string",
"null"
],
"default": null
},
"text_fields": {
"type": "array",
"items": {
"type": "string"
}
},
"title_weight": {
"type": "number",
"format": "float"
},
"updatable_fields": {
"type": "array",
"items": {
"type": "string"
}
},
"when_to_use": {
"type": "string"
},
"write_rules": {
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false,
"required": [
"name",
"description",
"when_to_use",
"sections",
"metadata_fields",
"title_weight",
"text_fields",
"hierarchy_relationship",
"updatable_fields",
"health_required_fields",
"staleness_threshold_days",
"write_rules"
],
"$defs": {
"ConstraintDef": {
"description": "One declared keep-health constraint on a type — the constraint\nvocabulary (agent-toolbox plan 07). Declarations travel sealed with\nthe schema package and are rendered on the `memstead_schema`\nresponse at BOTH verbosity levels (a hidden legality condition is a\ndefect class of its own). The `kind` tag is closed: an unknown kind\nfails deserialization, so no declaration can load and be silently\nignored. Forms land vertically — a form is only declarable once the\nengine evaluates it.",
"oneOf": [
{
"description": "Form 1 — conditional requirement: `field` (a metadata field or\nsection key of this type) is required whenever `when_field`\nholds `when_value` (\"`status: checked` requires `checked_by`\").",
"type": "object",
"properties": {
"field": {
"description": "The field or section that becomes required.",
"type": "string"
},
"kind": {
"type": "string",
"const": "requires_when"
},
"severity": {
"$ref": "#/$defs/ConstraintSeverity",
"default": "warn"
},
"when_field": {
"description": "The metadata field whose value triggers the requirement.",
"type": "string"
},
"when_value": {
"description": "The triggering value (validated against `when_field`'s\nenum, when it declares one).",
"type": "string"
}
},
"additionalProperties": false,
"required": [
"kind",
"field",
"when_field",
"when_value"
]
},
{
"description": "Form 2 — uniqueness: the tuple of metadata-field values named\nin `fields` is unique among entities of this type within one\nmem. Entities missing any of the fields carry no tuple and are\nnot compared. Defaults to `block` — the whole point of the\ndeclaration is preventing the duplicate at write time.",
"type": "object",
"properties": {
"fields": {
"description": "The metadata fields forming the unique tuple (each must be\na declared metadata field of this type).",
"type": "array",
"items": {
"type": "string"
}
},
"kind": {
"type": "string",
"const": "unique"
},
"severity": {
"$ref": "#/$defs/ConstraintSeverity",
"default": "block"
}
},
"additionalProperties": false,
"required": [
"kind",
"fields"
]
},
{
"description": "Form 3 — enum-from-neighbour: the legal values of `field` are\nthe bullet-list entries (`- value` lines) of the `section`\nsection on the entity reached from this one via a `rel_type`\nedge. A set value with no backing entry in any reached\nneighbour's section — including the no-neighbour and\nmissing-section cases, where nothing can back it — is a\nviolation.",
"type": "object",
"properties": {
"field": {
"description": "The metadata field whose values the neighbour enumerates.",
"type": "string"
},
"kind": {
"type": "string",
"const": "enum_from_neighbour"
},
"rel_type": {
"description": "The outgoing rel-type that reaches the enumerating entity.",
"type": "string"
},
"section": {
"description": "The section key on the reached entity whose bullet entries\nare the legal values.",
"type": "string"
},
"severity": {
"$ref": "#/$defs/ConstraintSeverity",
"default": "warn"
}
},
"additionalProperties": false,
"required": [
"kind",
"field",
"rel_type",
"section"
]
},
{
"description": "Form 5 — status propagation: when `field` on an entity of this\ntype holds `value` (the terminal value), every entity reaching\nit — transitively — via `rel_type` edges in `direction` is\ntainted; tainted entities surface as health findings naming\ntheir tainting ancestor. Always warn-tier: the taint arises\nfrom the ancestor's *later* change, so it can never refuse the\ndescendant's historical write (the loader refuses a `block`\ndeclaration on this form rather than accepting a promise the\nengine will not keep).",
"type": "object",
"properties": {
"direction": {
"description": "Which direction reaches the dependents: `incoming` taints\nthe entities whose `rel_type` edges point at the terminal\nentity (and their dependents, transitively); `outgoing`\nthe entities the terminal entity points at.",
"$ref": "#/$defs/PropagationDirection"
},
"field": {
"description": "The status metadata field on this (the tainting) type.",
"type": "string"
},
"kind": {
"type": "string",
"const": "status_propagation"
},
"rel_type": {
"description": "The rel-type the taint travels along.",
"type": "string"
},
"severity": {
"$ref": "#/$defs/ConstraintSeverity",
"default": "warn"
},
"value": {
"description": "The terminal value that starts the taint (validated\nagainst `field`'s enum, when it declares one).",
"type": "string"
}
},
"additionalProperties": false,
"required": [
"kind",
"field",
"value",
"rel_type",
"direction"
]
}
]
},
"ConstraintSeverity": {
"description": "Uniform severity for the constraint vocabulary — one model across\nevery constraint form, never five ad-hoc ones. `warn` produces a\nhealth finding only; `block` additionally refuses at write time\n(and still surfaces pre-existing violations in health). Severity\napplies to every write surface uniformly — operator-mode bypasses\nallowlists, never validation.",
"oneOf": [
{
"description": "Health finding only (and, where a write-time warning exists,\nthat warning). The default for every form except uniqueness.",
"type": "string",
"const": "warn"
},
{
"description": "Write-time refusal plus health finding for pre-existing\nviolations.",
"type": "string",
"const": "block"
}
]
},
"DueAxis": {
"description": "A type's declared **due axis** (first-author-path plan 08): which\nof its fields carry deadline semantics, so the engine's due-brief\n(`memstead due`) can render \"what is due next\" without knowing any\ndomain vocabulary. Validated at schema load: `date_field` must be\na date-typed metadata field of the type, `status_field` an\nenum-typed one, every `open_values` entry a member of that enum,\nand `lead_section` (optional — rendered as \"what must happen\nfirst\") a declared section key. The axis is rendering-only: it\nnever enforces anything (constraints own enforcement) and the\nengine never advances a date (the agent loop is the runtime).",
"type": "object",
"properties": {
"date_field": {
"description": "Date-typed metadata field holding the deadline.",
"type": "string"
},
"lead_section": {
"description": "Optional section key whose content renders with each entry as\n\"what must happen first\".",
"type": [
"string",
"null"
]
},
"open_values": {
"description": "The `status_field` values under which the entity counts as\nstill open (due-relevant). Every entry must be declared in the\nfield's `enum_values`.",
"type": "array",
"items": {
"type": "string"
}
},
"status_field": {
"description": "Enum-typed metadata field holding the lifecycle status.",
"type": "string"
}
},
"additionalProperties": false,
"required": [
"date_field",
"status_field",
"open_values"
]
},
"Exemplar": {
"description": "One canonical exemplar entity for a type — a complete entity in the\nmem markdown shape: title, metadata overrides, section bodies, and\nrelationship entries with placeholder targets. Engine-validated at\nschema install/seal through the real create path, so it can never\nteach a shape the validator would refuse.",
"type": "object",
"properties": {
"metadata": {
"description": "Metadata overrides, keyed by declared field key — validated\nlike a real create's metadata (enums included). Engine-stamped\nfields (`created_date`, …) are omitted; the engine fills them.",
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"relations": {
"description": "Relationship entries with PLACEHOLDER targets: each `to` is a\nbare slug (no `mem--` prefix — an exemplar lives outside any\nmem); validation checks rel-type legality and shape, never\ntarget existence.",
"type": "array",
"items": {
"$ref": "#/$defs/ExemplarRelation"
}
},
"sections": {
"description": "Section bodies keyed by section key. Required sections must all\nbe present — the validator enforces it like any create.",
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"title": {
"description": "The exemplar entity's title (drives the id slug exactly as a\nreal create would).",
"type": "string"
}
},
"additionalProperties": false,
"required": [
"title"
]
},
"ExemplarRelation": {
"description": "One relationship entry on an [`Exemplar`].",
"type": "object",
"properties": {
"description": {
"description": "Optional per-edge description — validated against the\nrel-type's `per_edge_description` posture.",
"type": [
"string",
"null"
]
},
"to": {
"description": "Placeholder target: a bare slug, scoped to the exemplar's own\n(virtual) mem at validation time.",
"type": "string"
},
"type": {
"description": "Relationship type (UPPER_SNAKE_CASE; validated against the\nschema's declared vocabulary).",
"type": "string"
}
},
"additionalProperties": false,
"required": [
"to",
"type"
]
},
"FieldType": {
"type": "string",
"enum": [
"string",
"number",
"date",
"boolean"
]
},
"Filterable": {
"type": "string",
"enum": [
"none",
"equality",
"range"
]
},
"MetadataFieldDef": {
"description": "A metadata (frontmatter) field.",
"type": "object",
"properties": {
"auto_timestamp": {
"type": "boolean",
"default": false
},
"default_value": {
"type": [
"string",
"null"
],
"default": null
},
"description": {
"type": "string"
},
"enum_values": {
"type": [
"array",
"null"
],
"default": null,
"items": {
"type": "string"
}
},
"field_type": {
"$ref": "#/$defs/FieldType"
},
"filterable": {
"$ref": "#/$defs/Filterable",
"default": "none"
},
"init_timestamp": {
"type": "boolean",
"default": false
},
"key": {
"type": "string"
},
"required": {
"description": "Whether an entity must carry this field — **absence means\noptional**, the same rule sections follow. `required: true`\nrefuses a create that leaves the field unset\n(`REQUIRED_FIELD_UNSET`); a required field with a\n`default_value` (or an `init_timestamp`) is auto-filled and\ntherefore never refused — required-with-default means \"always\npresent\", not \"caller must type it\". Replaces the retired\n`optional:` key (opposite polarity): sealed schemas carrying\n`optional` keep loading with inverted-but-equivalent\nsemantics; authoring refuses it naming this key.",
"type": [
"boolean",
"null"
]
},
"serialization": {
"$ref": "#/$defs/Serialization",
"default": "default"
}
},
"additionalProperties": false,
"required": [
"key",
"description",
"field_type"
]
},
"PropagationDirection": {
"description": "Traversal direction for [`ConstraintDef::StatusPropagation`].",
"type": "string",
"enum": [
"incoming",
"outgoing"
]
},
"RequiredCardinality": {
"description": "Required-cardinality variants. `AtLeastOne` is the only variant\nshipped initially; `ExactlyOne` is the obvious next variant but is\nnot yet wired.",
"type": "string",
"enum": [
"at_least_one"
]
},
"RequiredOutgoing": {
"description": "One outgoing-edge requirement block on a type definition. Lists one\nor more relationship names and a cardinality constraint they must\njointly satisfy. The schema author groups multiple alternative\nrelationships into a single block when \"any of these\" satisfies the\nrule (e.g. `[CHOSEN, REJECTED]` together with `at_least_one` would\nrequire at least one outgoing edge across both names — but the\nplanning schema lists each as its own block instead, so each block\ngets its own warning entry).",
"type": "object",
"properties": {
"cardinality": {
"$ref": "#/$defs/RequiredCardinality"
},
"relationships": {
"description": "Edge names the rule applies to. Loader validates each against\nthe schema's declared relationship vocabulary; unknown names\nraise `SchemaLoadError::UndeclaredRelationship`.",
"type": "array",
"items": {
"type": "string"
}
},
"severity": {
"description": "Constraint severity (form 4 of the constraint vocabulary):\n`warn` (the historical default — health finding +\n`MISSING_REQUIRED_OUTGOING` write-time warning) or `block`\n(write-time refusal when a create/update would land, or a\nrelate-remove would leave, the entity below cardinality).",
"$ref": "#/$defs/ConstraintSeverity",
"default": "warn"
}
},
"additionalProperties": false,
"required": [
"relationships",
"cardinality"
]
},
"SectionDef": {
"description": "A section within an entity (e.g. \"Claim\", \"Evidence\").",
"type": "object",
"properties": {
"catch_all": {
"type": "boolean",
"default": false
},
"content": {
"description": "Declared markdown shape (section-format vocabulary, plan 08):\na flat content expression over the mdast block vocabulary —\nsee [`crate::content_expr::ContentExpr`]. Absent = free-form,\nexactly the pre-declaration behavior. Validated and compiled\nat schema load ([`SectionDef::compiled_content`]).",
"type": [
"string",
"null"
]
},
"description": {
"type": [
"string",
"null"
],
"default": null
},
"example": {
"description": "One conforming snippet, echoed verbatim in every format\nrefusal — for an agent, a conforming example outperforms any\ngrammar string.",
"type": [
"string",
"null"
]
},
"format_severity": {
"description": "Severity of format violations (plan 07's uniform model).\nDefault `block`: a shape violation is deterministic and\none-round-trip repairable (the enum-value analogy) — `warn`\nstays available per section.",
"$ref": "#/$defs/ConstraintSeverity"
},
"heading": {
"type": "string"
},
"item_pattern": {
"description": "Regex applied to the repeating unit of the declared `content`\n(list items with lazy continuation joined; paragraph source\nlines). Implicitly anchored `^…$`; named capture groups name\nthe parts in refusal payloads. Legal only when `content`\ncontains exactly one of `list` / `paragraph`.",
"type": [
"string",
"null"
]
},
"key": {
"type": "string"
},
"required": {
"description": "Whether an entity must carry this section — **absence means\noptional**, the same rule metadata fields follow. `required:\ntrue` refuses a create without the section\n(`MISSING_REQUIRED_SECTION`).",
"type": "boolean",
"default": false
},
"search_weight": {
"type": "number",
"format": "float"
},
"table": {
"description": "Table contract — only legal when `content` contains `table`.",
"anyOf": [
{
"$ref": "#/$defs/TableFormat"
},
{
"type": "null"
}
]
},
"write_rules": {
"type": "array",
"default": [],
"items": {
"type": "string"
}
}
},
"additionalProperties": false,
"required": [
"key",
"heading",
"search_weight"
]
},
"Serialization": {
"type": "string",
"enum": [
"default",
"csv_array",
"omit_when_falsy"
]
},
"TableFormat": {
"description": "The table contract of a format-declared section: `columns` pins\nheader names and order; `column_patterns` maps column name → regex\nper cell (implicitly anchored). Column-count enforcement is ours by\ndecision — GFM silently pads/truncates short or long rows, so a\nrow with the wrong cell count is *our* refusal, not the parser's.",
"type": "object",
"properties": {
"column_patterns": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"columns": {
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false,
"required": [
"columns"
]
}
}
}