Template¶
A template declares the shape of a fact: its name and the typed slots
that facts of that kind carry. Templates compile to CLIPS deftemplate
constructs and live in the MAIN module regardless of which rule module
references them. For the conceptual role of templates among Fathom's five
primitives, see Five Primitives.
Top-level fields — TemplateDefinition¶
| Field | Type | Default | Required | Description |
|---|---|---|---|---|
name |
str |
— | yes | CLIPS identifier. Must match ^[A-Za-z_][A-Za-z0-9_\-]*$. Emitted as (deftemplate MAIN::<name> …). |
description |
str |
"" |
no | Author-facing prose. Not emitted to CLIPS. |
slots |
list[SlotDefinition] |
— | yes | One or more slot definitions. Empty list is accepted by Pydantic but rejected at compile time. |
ttl |
int \| None |
None |
no | Fact-expiry metadata (seconds). Not emitted to CLIPS; reserved for runtime fact-expiry. |
scope |
Literal["session", "fleet"] |
"session" |
no | Authoring hint consumed by the fleet layer. Not emitted to CLIPS. |
The name field is validated by _name_must_be_clips_ident at model
construction and re-checked (for emptiness) by compile_template.
Slot fields — SlotDefinition¶
| Field | Type | Default | Required | Description |
|---|---|---|---|---|
name |
str |
— | yes | Slot identifier. No regex validator is applied in the current model; the compiler emits it verbatim into (slot <name> …). |
type |
SlotType |
— | yes | One of string, symbol, float, integer. See SlotType. |
required |
bool |
False |
no | If True, FactManager rejects asserts that omit this slot with a ValidationError (src/fathom/facts.py:405-414). Not emitted to CLIPS; the rule-RHS assert path bypasses this check because it doesn't run through FactManager._validate. |
allowed_values |
list[str] \| None |
None |
no | Emitted only for string and symbol slots (see _CLIPS_ALLOWED_MAP). Silently ignored for numeric slots. |
default |
str \| float \| int \| None |
None |
no | Emitted as (default <value>). String defaults are CLIPS-quoted and escaped; numeric defaults are emitted raw. |
SlotType enum¶
SlotType is a StrEnum with four members. The compiler maps each to a
CLIPS type keyword via _CLIPS_TYPE_MAP and — for the two symbolic
types — to an allowed-values directive via _CLIPS_ALLOWED_MAP.
| YAML value | CLIPS type keyword | Allowed-values directive |
|---|---|---|
string |
STRING |
allowed-strings |
symbol |
SYMBOL |
allowed-symbols |
float |
FLOAT |
(none — silently dropped) |
integer |
INTEGER |
(none — silently dropped) |
A float slot accepts a Python int as well as a float; FactManager
widens it before it reaches CLIPS, which types 1 as INTEGER and would
otherwise reject it. bool is refused for both. An integer slot takes
only int, or a float with no fractional part.
Source: src/fathom/compiler.py lines 29–40.
CLIPS emission¶
compile_template (in src/fathom/compiler.py) emits a multi-line
deftemplate form. Every template is scoped to the MAIN module;
per-module scoping happens on defrule, not on deftemplate.
YAML input¶
templates:
- name: access-request
description: An agent's request to perform an action on a resource.
slots:
- name: subject
type: symbol
- name: action
type: string
allowed_values: [read, write, delete]
- name: amount
type: integer
default: 0
CLIPS output¶
(deftemplate MAIN::access-request
(slot subject (type SYMBOL))
(slot action (type STRING) (allowed-strings "read" "write" "delete"))
(slot amount (type INTEGER) (default 0)))
Emission rules¶
- First line:
(deftemplate MAIN::<name>. - Each slot is indented four spaces and wrapped as
(slot <name> <parts>). - Slot parts are emitted in this fixed order:
(type <CLIPS_TYPE>),(<allowed-directive> <values>)(string/symbol only),(default <value>). - For
stringslots, eachallowed_valuesentry and any string default is run through_escape_clips_string(backslash first, then double-quote) and wrapped in"…".symbolallowed values are emitted unquoted. - Numeric defaults (
int,float) are emitted viastr(slot.default)with no quoting. - The template closes with
)on its own line.
Validators¶
Pydantic-level rejections (raised at TemplateDefinition(...) or during
YAML load):
- Any key not listed in the tables above, on either model — both
TemplateDefinitionandSlotDefinitionareextra="forbid", so a misspelled field is an error rather than a silently dropped line. TemplateDefinition.namethat does not match^[A-Za-z_][A-Za-z0-9_\-]*$→ValueErrorfrom_name_must_be_clips_ident.SlotDefinition.namethat does not match the same pattern →ValueErrorfrom_validate_clips_ident.- On a
symbolslot, anallowed_valuesentry that is not a CLIPS identifier →ValueError. Symbol allowed values are emitted unquoted, so an unchecked entry closes thedeftemplateand appends constructs of its own. - On a
symbolslot, a stringdefaultthat is not a CLIPS identifier →ValueError, for the same reason. - On an
integerorfloatslot, a stringdefaultof any kind →ValueError: the value is interpolated verbatim, so it has to be numeric.
string slots are exempt from the last three: their values are escaped and
quoted on the way out, so no content in them can escape the construct.
Compile-time rejections (raised by Compiler.compile_template):
namethat is an empty string →CompilationErrorwithconstruct="template:<empty>".slotsis an empty list →CompilationErrorwithconstruct="template:<name>". Note: theslotsfield has no Pydanticmin_length=1constraint, so an empty list passes model validation and only fails at compile time.
What is not emitted¶
These YAML fields are accepted by the model but do not appear in the
compiled CLIPS deftemplate:
TemplateDefinition.description— metadata only.TemplateDefinition.ttl— reserved for runtime fact-expiry; has no CLIPS counterpart. A template withttl: 300emits the samedeftemplateas one without.TemplateDefinition.scope— consumed by the fleet layer, not the compiler.SlotDefinition.required— the flag itself is not emitted to CLIPS. Runtime enforcement happens inFactManager._check_required(src/fathom/facts.py:405-414) on the SDK and REST paths, but it is not checked on the rule-RHS assert path.SlotDefinition.allowed_valuesonfloatorintegerslots — silently dropped because these types have no entry in_CLIPS_ALLOWED_MAP.
If you need one of these to influence runtime behavior today, enforce it
in a rule (for example, an explicit allowed-values-style test CE for a
numeric slot) rather than relying on the template to do so.
See also¶
- Five Primitives — conceptual overview of templates, facts, rules, modules, and functions.
- YAML Compilation — how YAML documents are loaded and compiled to CLIPS source.
- Audit & Attestation — how asserted-fact slots surface in audit records.