Include syntax and settings¶
An include is a tag in a node's text that is replaced by another node's text when the node is read. For what that means and why, see How includes work.
Status words on this page: built means merged on main and described from
the code and the product specs; staged means accepted but with a different
effect than the final one; not built means do not rely on it. Merged
through hadron-server#2065 (grammar, write refusals) and
hadron-server#2118
(the read side), and
hadron-server#2226
(the writer's read check).
Grammar¶
INCLUDE = "{{>" [space] TARGET [space SETTING]... [space] "}}"
TARGET = NODE-URN | LOC
NODE-URN = hrn:node:<root>:<memory>:<loc>
LOC = a loc in the includer's own memory, no whitespace
SETTING = "rev=" N | "approved" | "signed"
N = a positive integer
- A target that starts with
hrn:node:is a node URN in the flat form, and it always names that exact node, in any memory the reader can read. Any other target is a loc, and it names only a node in the includer's own memory. - Write the flat form only. The product specification does not accept the older
form (
hrn:node:<root>::<memory>::<loc>); whether a write refuses it today is not something this page claims. - Settings go in any order and each appears at most once.
Settings¶
With no setting an include serves the target's latest revision.
| Setting | Unpinned | With rev=N |
State |
|---|---|---|---|
| none | the latest revision | revision N | built |
approved |
the newest approved revision | revision N, which must be approved | not built: renders the notice |
signed |
the newest signed revision | revision N, which must be signed | staged: stored and ignored until signing ships, served as if absent |
approved signed |
newest revision both approved and signed | revision N, both | staged: behaves as approved, so renders the notice |
How rev=N resolves: a stored snapshot of revision N is served when one exists.
When none exists and N is the target's current revision, the current text is
served. A snapshot captured while the node lived in another memory is never
served, and does not fall back to the current text. A revision that cannot be
served is a broken include.
approved does not start working when a helper merges. It needs the renderer
wired to it and merged integration evidence, and this page will change only
then.
Which reads resolve¶
| Read | Resolves |
|---|---|
GraphQL Node.content (node, nextNode, findNodes, edge ends, mutation payloads) |
yes; raw: true returns the stored text |
GraphQL nodeBatch, bare content |
no, stored text; content(raw: false) resolves |
MCP hadron_get_node, hadron_get_nodes, hadron_get_next_node |
yes; raw: true returns the stored text |
Skill export (skillPlan), nodeExport(full: false), PDF and Google Doc export |
yes |
nodeExport(full: true), memory export |
no, stored text |
A past revision (NodeRevision.content) |
no, stored text |
| Search matching, the index and embeddings | built from the stored text, never resolved |
content returned on a search hit |
as any Node.content: resolved by default, stored with raw: true |
| Run, chat and session renders | yes |
All resolving reads use the reader's own access. The command-line behaviour is documented separately once the CLI release that states it explicitly lands.
Broken includes¶
An include that cannot be shown renders [Include unavailable], identical for a
missing target, a deleted one, one the reader may not read, a revision that
cannot be served, a chain deeper than 10, and a cycle. The rest of the node
resolves. Nesting counts from the includer: its own tags are level 1, ten
nested levels resolve, an eleventh is broken. A cycle is caught at its first
repeat.
Currently (code behaviour, not a platform promise): a target in an encrypted
memory with no key available shows the same notice, and chat-message and
record nodes are never resolved.
What a write does today¶
| Tag written | Today |
|---|---|
| Malformed tag | refused, INVALID_INCLUDE |
| Node that would include itself, directly or through other includes, by a tag the write adds or changes | refused, INCLUDE_CYCLE |
| A chain deeper than 10 | saves; reads show the notice past level 10 |
| Added or changed tag whose target is missing, deleted or unreadable to the writer | refused, INCLUDE_TARGET_UNAVAILABLE (one error for all three; names only the authored tag) |
| Tag already stored and unchanged, target now unreadable | no access recheck; saves, keeps its edge, the notice shows on read |
rev=N the target does not have |
accepted; the notice shows on read |
The node gets a derived edge of governed kind INCLUDE to each distinct target
that currently exists and is readable by the writer. A stored tag whose target
has since become unreadable keeps the edge it had and never gains a new one. Only the node's content owns these
edges: editing or deleting that edge directly is refused with
INCLUDE_EDGE_DERIVED.
Added or changed is decided against the node's stored text: kind, target
and settings are compared, so spacing inside the braces is not a change but a
settings change, or a different form of the tag for the same node, is. The
check is existence plus read access to the target's memory; making that
decision never decrypts the target. The check on added or changed tags
applies to GraphQL and MCP writes alike (create, update, batch replace, merging
one node into another with mergeNodes, restore a revision,
search-and-replace). Git sync and the controller's root-node writes skip the
access check and run the syntax and cycle checks only. Restoring a published
Agent snapshot runs no include check at all; it only re-derives the edges.
Every save still parses the submitted text, so a malformed tag is refused whether or not it is new. The cycle check starts from the tags the write adds or changes. A cycle that formed later through tags you did not touch does not block an unrelated save.
A memory merge (mergeMemories) is never refused over include tags; reads
conceal what a reader cannot see.
Not built: copying a node or memory (clone, extract) does not yet run this check on the copied tags (hadron-server#2167).
Known gap¶
A tag inside a code span or fenced block is kept literal by the parser, but the template step still drops it on read, so an example of the syntax disappears when the node is read. See How includes work.
Related¶
- How includes work
- Mustache template syntax: variables, and partials.
- GraphQL API reference