HEART Part 3: Information Model#
What a HEART tells you, and in what shape
Author: Milind K. Patil, Syncaissa Systems Inc. Status: Working draft 1, November 2026. Part of the HEART Standards Family. Version: 1.0 (draft) Read first: Part 1: Basics, for the words used here. Track: Basic: "In one minute", Sections 2, 3.1, 3.2, 3.4, 4, 5.1 to 5.4, 5.6, 5.7, 7 and 9. Intermediate adds 3.3, 3.7, 5.8, 8, 10 and 11. Advanced adds 3.5, 3.6 and 6. Sections 5.5 and 12 to 14 are reference.
In one minute#
Every HEART says things about itself using seven main messages. Six travel as messages of their own; the Capability lives inside the Card:
| Message | In one sentence |
|---|---|
| Card | Who I am, and what I can do. |
| Pulse | How I am right now. |
| Rhythm | What has happened, in order. |
| Arrhythmia | Something is wrong. Here is what, and why. |
| Capability | Something you can ask me to do. |
| Action | Something I am doing, or did, and who allowed it. |
| Receipt | Proof of what data I sent, and to whom. |
Every message travels in the same envelope, and uses the same shared word lists. For example, health is always one of healthy, degraded, failing or unknown.
Problems are named with fault codes from one shared list, such as storage.full or sensors.drift. A fault code means the same thing on every product, the way OBD-II code P0300 means "engine misfire" on every car. Outside messages it is written HEART storage.full, and each standard code also has a short number, such as H-04002.
This part describes what is said. It does not say how messages travel (that is Part 4), so the same information can be carried by HTTP, MQTT or, later, industrial standards such as OPC UA.
1. What an "information model" is#
Think of a paper form, like a doctor's check-up form. It has fixed boxes: name, date, blood pressure, temperature, notes. Every doctor fills in the same boxes, so any other doctor can read it.
An information model is the same idea for machines. It says:
- which forms exist: Card, Pulse, Arrhythmia and so on,
- which boxes each form has, and which are required,
- what may be written in each box: a number, a date, or one word from a fixed list.
It does not say how the form is delivered: by post, by fax or by hand. That is Part 4.
Keeping the two apart matters. The forms stay the same while delivery methods change. A tiny sensor and a cloud server can fill in the same forms, and deliver them in completely different ways.
Where the exact definitions live#
This part explains the model in words. The exact, machine-checkable definitions are the JSON Schema files in schemas/1.0/, with one example of each message in examples/. If this part and a schema disagree, that is a mistake in the standard, and should be reported.
2. The envelope#
Every message a HEART sends about itself goes inside the same envelope. Like the outside of a parcel, the envelope says who it is from, what is inside and when it was sent. The message itself sits inside, in data.
HEART does not invent its own envelope. It uses CloudEvents, a widely used standard for event messages, so existing tools can already route HEART messages.
{
"specversion": "1.0",
"id": "c7f3a9e2-1b44-4d0e-9a51-2f6c8e1d7b90",
"source": "/heart/com.example.cool-air.ac-controller/unit-12",
"type": "heart.pulse",
"time": "2026-11-08T10:00:00Z",
"datacontenttype": "application/json",
"heartversion": "1.0",
"heartprivacy": "public",
"data": {
"system": { "id": "com.example.cool-air.ac-controller", "name": "CoolAir AC Controller", "version": "0.1.0" },
"status": "healthy",
"sequence": 90,
"intervalSec": 60
}
}
| Box | What it holds |
|---|---|
id | A unique number for this message. If the message is sent again, it keeps the same id, so duplicates can be spotted. |
source | Which system sent it: /heart/ followed by the system's ID, and optionally which copy of the system (the instance). |
type | Which message is inside, such as heart.pulse (Section 3). |
time | When it was created, in the standard internet time format (RFC 3339). |
datacontenttype | Always application/json. |
heartversion | Which version of HEART it follows, such as 1.0. |
heartprivacy | The most private level of anything inside, after filtering. |
heartredacted | true if some fields were removed because the receiver may not see them. |
data | The message itself. |
All of these are required except heartredacted, which is present only when something was removed.
Requests sent to a system, such as "invoke this capability", are not statements about the system, so they do not need the envelope over HTTP. Part 4 explains how they are carried.
3. The seven main messages#
| Message | type | Answers | Schema |
|---|---|---|---|
| Card | heart.card | Who are you, and what can you do? | card |
| Pulse | heart.pulse | How are you right now? | pulse |
| Rhythm event | heart.rhythm | What happened? | rhythm |
| Arrhythmia | heart.arrhythmia | What is wrong, and why? | arrhythmia |
| Capability | (inside the Card) | What can I ask you to do? | in card |
| Action | heart.action | What are you doing, and who allowed it? | action |
| Receipt | heart.receipt | What data did you send, and to whom? | receipt |
Six of these have a type of their own. The Capability does not: it is a part of the Card. One more message belongs with them: Evolution (heart.evolution, schema evolution) says which AI model is pinned and how new candidates tested. It is part of the Full Feature Set ("AI model tracking", Section 7), so the API Standard lists it among its seven core types, in place of the Capability.
There are also smaller messages for optional modules (Section 8), such as heart.component and heart.property. The prefix heart. is reserved for the standard. A product may add its own message types, named with its own reversed domain name, such as com.coolair.service-report (Section 9).
In the tables below, a bold box name means the box is required.
3.1 Card: who I am#
The Card is the system's identity card. A consumer reads it first, and usually only once.
{
"system": { "id": "com.example.cool-air.ac-controller", "name": "CoolAir AC Controller",
"version": "0.1.0", "owner": "Example Climate Ltd" },
"level": "full",
"bindings": ["http-server", "unix-socket"],
"pulse": { "intervalSec": 30 },
"modules": ["control", "properties", "custom", "core"],
"profiles": ["hvac"],
"autonomy": { "level": 2, "higher": [ { "level": 4, "areas": ["connectivity", "storage"] } ] },
"capabilities": [ { "name": "calibrate", "risk": "write", "...": "see Section 3.5" } ]
}
| Box | What it holds |
|---|---|
system | id (unique name, Section 9), name (for people), version, and optionally instance, alias and owner. |
level | The system's Feature Set: basic, connected or full (Section 7). The box keeps its old name, level, for now. |
bindings | How the system can be reached, such as http-server or mqtt (Part 4). |
pulse | intervalSec: how often a Pulse is sent. |
endpoints | Addresses: the HEART API, a Gateway, and other standards the system also speaks (MCP, A2A, OpenTelemetry). |
modules | Optional modules the system has (Section 8). |
profiles | Product profiles it follows, such as hvac (Part 8). |
autonomy | Its promised Autonomy Level and scope (Part 2). |
capabilities | What it can be asked to do (Section 3.5). |
events | Product-specific Rhythm events it may send. |
shares | Who may read each kind of message: keys are message types without heart., such as pulse, and values are public, internal or private (Part 5). |
valve | Privacy settings, including the most private level that may ever reach an AI model, and classifications that raise the level of further boxes, each written <message>:<JSON Pointer>, such as rhythm:/attributes/recordingPath (Part 5). |
ai | Which ways of thinking it uses, and what it does offline. |
core | Its HEART Core size class and ways of thinking (Part 7). |
auth | Where to log in (Part 4). |
identity | The system's identity key, which signs the Card (Part 4). |
heartVersion | Version of the HEART implementation it runs. |
template | The HEART template it was built from, if any. |
limits | Size limits, for small devices. |
labels | Owner's tags, used to pick groups of systems. |
ext | The product's own extra information (Section 10). |
The Card is shown filtered. An anonymous caller sees only the public parts. A capability is shown only to callers allowed to use it.
3.2 Pulse: how I am#
The Pulse is a short message sent at a regular interval, or read on demand. Its first job is to say "I'm alive". Its second is to say how the system is.
| Box | Who can see it | What it holds |
|---|---|---|
system | Anyone | Which system |
status | Anyone | healthy, degraded, failing or unknown (Section 4) |
sequence | Anyone | A counter that goes up by one with each Pulse, so a missed one can be spotted |
intervalSec | Anyone | Seconds until the next Pulse |
reconnaissance | Owner's side | Current state, the last event, and the Vitals: uptime, error rate, response time, CPU and memory use |
ai | Owner's side | How it is thinking right now (rules-only, local or cloud), which model, and today's calls and cost against the budget |
tending | Owner's side | How many problems are open, and the last action taken |
autonomy | Owner's side | current: its Autonomy Level right now (0 to 5), and limitedBy: why it is lower than promised, if it is: stopped, no-model, no-network, budget, degraded or brain-cut (Part 2). Always present at Full. |
evolving | Owner's side | HEART version, and when its AI model last changed |
So anyone can learn whether a system is healthy, and only the owner's side learns the details.
Vitals are a summary. Detailed measurements belong in a monitoring standard such as OpenTelemetry, not in the Pulse.
Flatline. If no Pulse has arrived for three intervals, a consumer MUST treat the system's status as unknown. A Gateway or monitoring system SHOULD then raise an Arrhythmia with code system.flatline (Section 5).
3.3 Rhythm: what happened#
The Rhythm is the system's history: a list of events, oldest first. Each event is one thing that happened.
| Box | What it holds |
|---|---|
sequence | Its position in the history. Always increases. |
name | What happened, as lower-case words (letters, digits, - or _) joined by dots, such as component.restarted |
category | Which family the event belongs to (below) |
severity | How important it is: info, warning, error or critical |
component | Which part of the system it concerns |
summary | A short sentence for people |
attributes | Details. Private by default. |
| Category | Covers |
|---|---|
system | Start, stop, version changes |
state | Changes of state or status |
component | A part starting, stopping or failing |
ai | Calls to AI models |
tending | Problems noticed and actions taken |
evolving | AI models adopted or rejected |
valve | Data released or blocked |
circulation | Messages to and from other systems |
security | Logins, permissions, suspicious activity |
domain | The product's own events, such as wash.finished |
The event names starting with system., state., component., ai., tending., evolving., valve., circulation. and security. are reserved for the standard. Product events use any other name and the category domain.
The standard event types (the values of name reserved for the standard) are listed in Section 5.8. An event type says that something happened. It is not a fault code.
If old history has been deleted to save space, the system MUST say so (Part 4 explains how), so nobody mistakes a gap for "nothing happened".
3.4 Arrhythmia: something is wrong#
An Arrhythmia is an alert. It is the most important message, because it carries the system's own view of what is wrong.
It answers three questions:
- What is wrong? The fault
code, for exampleactuators.short-cycling. - Why? The
cause, with its own fault code where known, for examplesensors.drift. - What is being done? Its
status, and anyactions.
{
"id": "5b1e0c9a-7d2f-4b8e-9c31-0a6f2e4d8b17",
"code": "actuators.short-cycling",
"severity": "warning",
"status": "open",
"detectedAt": "2026-11-08T09:58:00Z",
"component": "compressor",
"summary": "Compressor short cycling",
"explanation": "The compressor is starting and stopping too often (12 times in the last hour). Cooling may be weak. Your installer has been told.",
"cause": {
"code": "sensors.drift",
"component": "room-sensor",
"text": "The room sensor reads 3 °C higher than the return-air sensor.",
"confidence": "medium"
},
"evidence": [4012, 4013, 4019]
}
| Box | What it holds |
|---|---|
id | A unique ID for this alert |
code | What is wrong, as a fault code (Section 5) |
severity | info, warning, error or critical |
status | Where the alert is in its life (below) |
detectedAt | When it was first noticed |
resolvedAt | When it was fixed |
component | Which part has the problem |
summary | A few words for a list of alerts |
explanation | Plain language: what happened and what it means for the user. Required for Autonomy Level A1 (Part 2). |
cause | Why it happened: a fault code if one fits, the component, a plain-language text, and a confidence of low, medium or high. Required for A2. If the cause is not known, text says so and code is left out. |
evidence | The Rhythm events that led to this alert, so a person can check |
actions | Actions taken or proposed for it |
escalation | When and why a person was told |
acknowledgedAt, acknowledgedBy | When, and by whom, a person first acknowledged it |
silencedUntil | Until this time, the alert and repeats of its code are raised and listed but not notified (API Standard 5.1). Set by a silence or by maintenance |
kind | Deprecated. The older, broad alert type, kept so that existing software keeps working (Section 5.5). Optional. |
What is seen, and why. One problem often causes another. In the example, the compressor problem (code) is caused by a sensor problem (cause.code). Both come from the same list of fault codes. This is how a HEART tells you not only "something is wrong", but "this is the real reason".
An alert moves through these states:
open ──► acknowledged ──► tending ──► resolved
│ │ │ ▲
│ ▼ │ │
└──────► escalated ◄───────┘ │
└─────────────────────────┘
Any state may also move straight to resolved when the problem goes away by itself. acknowledged is reached through POST /arrhythmias/{id}/acknowledge. An escalated alert is resolved by the person it was handed to, or by the HEART once the problem is gone.
| Status | Meaning |
|---|---|
open | Noticed. Nobody has responded yet. |
acknowledged | A person has seen it. |
tending | The HEART is working on it. |
escalated | Handed to a person, because the HEART cannot or may not fix it. |
resolved | Fixed. |
3.5 Capability: what you can ask me to do#
A capability is something the system can be asked to do. Its boxes are a superset of an MCP tool, so any capability can also be offered to AI assistants as an MCP tool without changes.
{
"name": "calibrate",
"title": "Calibrate a sensor",
"description": "Recalibrates one temperature sensor against the reference sensor. Takes about 2 minutes.",
"inputSchema": { "type": "object", "required": ["sensor"], "properties": { "sensor": { "type": "string" } } },
"risk": "write",
"physical": false,
"reversible": true,
"idempotent": true,
"privacy": "internal",
"callers": ["human", "rule", "ai"],
"kind": "standard"
}
| Box | What it holds |
|---|---|
name | A short name of up to 128 letters, digits, _, . or -. Use a standard name where one exists (Section 6). |
title | A short title for people |
description | What it does, for people and for AI models deciding what to call |
inputSchema | The shape of what goes in (JSON Schema). Use {"type": "object"} with no properties when it takes nothing. |
outputSchema | The shape of what comes out (JSON Schema) |
risk | read, write or destructive (Part 1) |
physical | true if it moves, heats, powers or switches something in the real world |
reversible | Whether it can be undone |
idempotent | Whether doing it twice has the same effect as doing it once |
privacy | How private its results are: public, internal or private. A caller needs at least this clearance to see or use it. |
callers | Who may start it: human, peer, ai, rule. The default is human only. |
kind | standard (a standard name), product (the product's own, the default) or custom (from a custom definition, Part 8) |
guards | Checks made just before it runs: whenStates, whenStatus, requiresMaintenance, minIntervalSec, maxPerHour and interlocks (API Standard 14.4). Each interlock is a check: a signal (a Signal Table signal or a property of this system), an operator and a number, read from the device at that moment. A boolean reads as 1 or 0. An interlock that cannot be read counts as failed. |
3.6 Action: what I am doing#
When anyone asks for a capability, the system creates an Action. The Action records the whole story: who asked, who approved, what happened.
| Box | What it holds |
|---|---|
id | A unique ID (a UUID). The client may choose it, as actionId in its request, so it can compute the digest in advance (API Standard 6.3). |
capability or operation | What is being done: a capability name, or for any other API operation operation written "METHOD /path", such as "PUT /heart/v1/tending/autonomy". Exactly one of the two. |
status | Where it is in its life (below) |
risk | read, write or destructive: the risk the gate applied, which may be higher than the capability's own (API Standard 6.3) |
createdAt | When it was requested |
expiresAt | When it expires if still awaiting approval |
digest | SHA-256 fingerprint of what is being approved: system, id, capability or operation, arguments and physical (API Standard 6.3). It cannot change once approval is asked for. |
arguments | The inputs. Private. |
initiator | Who started it: kind is human, peer, ai or rule (a Playbook). For an AI, onBehalfOf says whose authority it is using. |
decision | Approve, deny or cancel, when, by whom, and how presence was proved (webauthn, ciba, device-button or remote-site) |
approvedBy | For a later step or the undo of a Trial: the first Action of that Trial, whose approval covered the whole plan (Core Standard) |
arrhythmiaId | The alert it is fixing, if any |
explanation | Plain language: what it does and why |
physical | Whether it affects the real world |
dryRun | true if it only reports what would happen |
progress, result, error | How it is going, and how it ended |
cancellable | Whether a cancel can still take effect |
nextReachableAt | For shutdown, sleep and restarts: when the system expects to answer again |
awaiting-approval ──► approved ──► running ──► succeeded
│ │ │ └─► failed
├──► denied │ └──► cancelling ──► cancelled
├──► expired └──► refused
└──► cancelled
denied: a person said no. refused: the system said no (a guard, a precondition or the autonomy mode). cancelling: a cancel takes effect at the next safe point; see API Standard 6.3, rule 5.
An Action that needs no approval starts at running. The rules for who must approve what are in Part 6.
3.7 Receipt: what data I sent#
Whenever non-public data leaves the system, to an AI provider, a Gateway or another system, the HEART writes a Receipt. It records who received the data, why, how private it was, how many bytes were sent, and a salted commitment to exactly what was sent: SHA-256 over a fresh random salt followed by the payload. The salt goes to the recipient, never into the Receipt, so nobody can guess short values back from it. A Receipt never contains the data itself.
Part 5 explains Receipts fully.
4. Shared word lists#
Many boxes accept only words from a fixed list. That is what lets a program understand any HEART without guessing.
| List | Words |
|---|---|
| Status | healthy, degraded, failing, unknown |
| Severity | info, warning, error, critical |
| Risk | read, write, destructive |
| Privacy level | public, internal, private, never-share |
| Confidence | low (a guess worth checking), medium (likely), high (well supported by the evidence given) |
| Initiator | human, peer, ai, rule |
| Arrhythmia status | open, acknowledged, tending, escalated, resolved |
| Action status | awaiting-approval, approved, running, cancelling, succeeded, failed, denied, refused, cancelled, expired |
| Component kind | service, process, sensor, actuator, board, module, library, store, network, other |
| Rhythm category | Section 3.3 |
| Area | Section 5.2 |
| Units | UCUM codes, the standard list of units of measure, such as Cel for degrees Celsius |
The four statuses mean exactly this:
| Status | Meaning | Air-conditioner example |
|---|---|---|
healthy | Working normally | Cooling as expected |
degraded | Working, but less well | Cooling, but weakly |
failing | Cannot do its main job | Not cooling at all |
unknown | Cannot tell yet | Just switched on, or no Pulse for a while |
5. Fault codes#
5.1 Why fault codes#
A message like "error 0x3F" means nothing outside one company. A message like "something is wrong" means nothing to a program. A fault code fixes both. It is a short, readable name for one specific problem, with one fixed meaning on every product.
A building dashboard watching 500 different devices can then count every storage.full across all of them, without knowing any of the brands.
HEART fault codes are words, not numbers. sensors.drift is easier to read and remember than S0402, and still easy for programs. For a small screen, a blinking light or a phone call, each standard code also has a short fixed number, its H-number, such as H-08005 for sensors.drift (Section 5.7). The name stays the main form.
5.2 How a code is built#
A fault code has two parts, separated by a dot:
sensors . drift
───┬─── ──┬──
area problem
The area is where the problem is. The areas are the same as in Part 2, plus system for the whole system:
| Area | Number | Covers |
|---|---|---|
system | 01 | The whole system |
power | 02 | Supply, battery, charging |
connectivity | 03 | Network and links to other systems |
storage | 04 | Disk, memory cards, databases |
performance | 05 | Speed, load, heat |
software | 06 | Crashes, versions, updates, settings |
ai | 07 | AI providers, models, budgets |
sensors | 08 | Readings and calibration |
actuators | 09 | Motors, valves, heaters, compressors |
data | 10 | Data quality, sync, backups |
security | 11 | Logins, certificates, tampering |
The number is the first part of the H-number of every code in that area (Section 5.7). A new area takes the next free number, 12 to 99.
The problem is one or more lower-case words joined by hyphens, such as nearly-full.
5.3 Three kinds of code#
| Kind | Looks like | Who defines it | Example |
|---|---|---|---|
| Standard | area.problem | This standard (Section 5.4) | storage.full |
| Profile | profile.problem | A product profile (Part 8) | hvac.refrigerant-low |
| Product | reversed domain name, then the problem | The product's maker | com.coolair.filter-clogged |
Rules:
- A system MUST use a standard code when one fits.
- If none fits, it SHOULD use a profile code, and only then a product code.
- Product codes MUST start with a domain the maker owns, so two makers never clash.
- A product code SHOULD be described in the Card's
events, with a sentence saying what it means. - Only standard codes have an H-number (Section 5.7). Profile and product codes have none: they are always written by name.
5.4 The standard fault codes#
This is the starting list. It will grow through the registry process (Section 11). Each code has its H-number beside it (Section 5.7). These codes are all faults: things that are wrong. Things that simply happen, such as a start or a restart, are event types (Section 5.8).
A page for each code, with its likely causes, how to fix or prevent it, and what a HEART does by itself, is at https://heartcortex.ai/faults/<code>, such as /faults/storage.full. The same facts are in one machine-readable file, fault-codes/1.0/fault-codes.json, checked against the tables below by tools/validate.py.
system: the whole system
| Code | H-number | Meaning |
|---|---|---|
system.main-function-lost | H-01001 | The system cannot do its main job. Every HEART MUST be able to detect this (Part 2, A1). |
system.main-function-degraded | H-01002 | The main job works, but less well than normal. |
system.flatline | H-01003 | No Pulse for three intervals. Raised by whoever is watching, not by the system itself. |
system.unexpected-restart | H-01004 | The system restarted without being asked to. |
system.heart-unwell | H-01005 | The HEART itself has a problem. The product may be fine. |
system.undo-failed | H-01006 | A change the HEART made could not be undone, so the system is left changed (HEART Core, Section 7.5). A person must look. |
power
| Code | H-number | Meaning |
|---|---|---|
power.supply-lost | H-02001 | Main power is gone. Running on battery or backup, if any. |
power.supply-unstable | H-02002 | Voltage or supply is outside the normal range. |
power.battery-low | H-02003 | The battery needs charging soon. |
power.battery-failing | H-02004 | The battery no longer holds charge as it should. |
connectivity
| Code | H-number | Meaning |
|---|---|---|
connectivity.link-lost | H-03001 | A network link is down. |
connectivity.link-unstable | H-03002 | A link keeps dropping or is very slow. |
connectivity.dependency-unreachable | H-03003 | An outside service or system it relies on cannot be reached. |
connectivity.clock-unsynced | H-03004 | The clock cannot be checked against a time source. |
storage
| Code | H-number | Meaning |
|---|---|---|
storage.nearly-full | H-04001 | Storage is almost full (the system states the threshold). |
storage.full | H-04002 | Storage is full. Writing new data fails. |
storage.write-failed | H-04003 | Data could not be written, for a reason other than being full. |
storage.wearing-out | H-04004 | Storage hardware is near the end of its life. |
storage.corrupted | H-04005 | Stored data or the file system is damaged. |
performance
| Code | H-number | Meaning |
|---|---|---|
performance.slow | H-05001 | Responses take much longer than normal. |
performance.overloaded | H-05002 | More work is arriving than can be handled. |
performance.backlog | H-05003 | A queue of waiting work keeps growing. |
performance.memory-exhausted | H-05004 | Memory is (nearly) used up. |
performance.too-hot | H-05005 | The device is overheating and may slow itself down. |
performance.overshoot | H-05006 | A controlled value goes past its target by more than the system's stated limit, such as a room getting colder than its setpoint. |
software
| Code | H-number | Meaning |
|---|---|---|
software.error-spike | H-06001 | Errors are happening far more often than normal. |
software.crashed | H-06002 | A program or component crashed. |
software.crash-loop | H-06003 | A component keeps crashing and restarting. |
software.update-failed | H-06004 | An update did not install. |
software.config-invalid | H-06005 | Settings are wrong or contradict each other. |
software.version-mismatch | H-06006 | Parts that must work together have incompatible versions. |
ai
| Code | H-number | Meaning |
|---|---|---|
ai.provider-unavailable | H-07001 | The AI model or provider cannot be reached. |
ai.cost-spike | H-07002 | AI spending is rising much faster than normal. |
ai.budget-near-limit | H-07003 | AI spending has reached 80% of the budget. |
ai.budget-exceeded | H-07004 | The AI budget is used up. AI calls are stopped. |
ai.model-drift | H-07005 | The AI model's answers have changed in quality or behaviour. |
ai.output-rejected | H-07006 | AI suggestions keep failing the HEART's checks. |
ai.lease-expired | H-07007 | The brain lease ran out without a renewal, so the system is brainless (API Standard 7.4). |
ai.brain-revoked | H-07008 | The brain was cut: by a Command Center signal, by the owner, or by the system's own tripwire. |
ai.answer-rejected | H-07009 | A brain answer was refused: unsigned, wrongly signed, for another request, or outside the lease. |
ai.disagreement | H-07010 | The brain's answer disagreed with the system's own checks on something safety-relevant. |
sensors
| Code | H-number | Meaning |
|---|---|---|
sensors.no-reading | H-08001 | A sensor gives no value. |
sensors.out-of-range | H-08002 | A reading is outside what is physically possible. |
sensors.stuck | H-08003 | A reading has not changed when it should have. |
sensors.noisy | H-08004 | Readings jump around far more than normal. |
sensors.drift | H-08005 | A sensor has slowly moved away from the true value. |
sensors.disagree | H-08006 | Two sensors that should agree do not. |
actuators
| Code | H-number | Meaning |
|---|---|---|
actuators.not-responding | H-09001 | A motor, valve or similar part does not react to commands. |
actuators.position-mismatch | H-09002 | It ended up somewhere other than where it was told to go. |
actuators.stalled | H-09003 | It is blocked or cannot move. |
actuators.short-cycling | H-09004 | It switches on and off far too often. |
actuators.overcurrent | H-09005 | It is drawing more current than it should. |
data
| Code | H-number | Meaning |
|---|---|---|
data.invalid | H-10001 | Data fails its checks. |
data.missing | H-10002 | Expected data has not arrived. |
data.out-of-sync | H-10003 | Two copies of the same data disagree. |
data.backup-failed | H-10004 | A backup did not complete. |
data.integrity-failed | H-10005 | Data has been changed or damaged when it should not have been. |
security
| Code | H-number | Meaning |
|---|---|---|
security.login-failures | H-11001 | Many failed login attempts. |
security.unexpected-access | H-11002 | Access from an unknown place, or at an unusual time. |
security.certificate-expiring | H-11003 | A security certificate expires soon. |
security.certificate-expired | H-11004 | A security certificate has expired. |
security.tamper-detected | H-11005 | Someone may have opened or altered the device or its software. |
security.policy-violation | H-11006 | Something broke the owner's rules, such as data that should not have left. |
security.anomaly | H-11007 | Unusual activity that may be an attack. |
5.5 The older kind box#
Before fault codes, alerts had a kind: one of 11 broad types. To keep existing software working, a system SHOULD send both code and kind during the change-over, when an old kind fits:
Old kind | Fault code |
|---|---|
error-spike | software.error-spike |
latency-spike | performance.slow |
resource-exhaustion | performance.memory-exhausted or storage.full |
cost-spike | ai.cost-spike |
dependency-failure | connectivity.dependency-unreachable |
model-unavailable | ai.provider-unavailable |
model-drift | ai.model-drift |
security-anomaly | security.anomaly |
policy-violation | security.policy-violation |
data-integrity | data.integrity-failed |
flatline | system.flatline |
kind will be removed in version 2.0.
5.6 Writing a code outside messages#
Inside a message, a code is written bare:
"code": "storage.full"
Outside messages, in logs, tickets, documents, screens and searches, it is written with HEART and a space in front: HEART storage.full. The word tells a reader, or a search engine, which list the code comes from. The same holds for profile and product codes, such as HEART hvac.refrigerant-low.
A program reading a message MUST NOT expect the prefix. A program writing a message MUST NOT add it.
5.7 H-numbers#
Each standard fault code has a short number for places where a name does not fit: a two-digit display, a blinking light, a code read out over the phone. It is written H-AACCC:
| Part | Meaning | Example |
|---|---|---|
H- | A HEART fault code | |
AA | The area's number (Section 5.2) | 04: storage |
CCC | The code's place in its area, from 001 | 002: full |
So H-04002 is storage.full, and H-01001 is system.main-function-lost.
Rules:
- An H-number is fixed for ever. It is never changed, never reused, and never given to another code, even if its code is deprecated.
- A new code takes the next free number in its area. A new area takes the next free area number.
- Only standard codes have H-numbers. Profile codes, product codes and event types have none.
- The name is the main form. Messages carry the name, never the H-number. A screen, label or manual MAY show the H-number beside the name, or alone where the name does not fit.
- A person can look up any H-number at
https://heartcortex.ai/system-code/H-AACCC, such as/system-code/H-04002. A part of a number, such asH-04, lists every code that starts with it.
5.8 Event types: not fault codes#
An event type names something that happened: the system started, a part was restarted, a message arrived. It is the name of a Rhythm event (Section 3.3). It is not a fault, and MUST NOT be used as an alert's code or cause.code.
Event types look like fault codes, area.name, but they are a separate list. They have no H-number. When an event shows that something is wrong, the HEART raises an alert with a fault code. For example, a component.failed event three times in 30 seconds may raise connectivity.dependency-unreachable, and a restart nobody asked for raises system.unexpected-restart beside the system.started event.
These standard event types are defined so far. Each has the category of its prefix (Section 3.3).
| Event type | Meaning |
|---|---|
system.started | The system started. |
system.restarted | The HEART resumed after a restart, from saved state. |
system.stopping | The system is shutting down on purpose. |
system.port.fallback | The standard port was taken, so the system runs on a fallback port. |
system.custom.changed | A custom definition was added, changed or removed. |
system.lifecycle.started, system.lifecycle.done, system.lifecycle.failed, system.lifecycle.cancelled | A lifecycle operation, such as a restart, started, finished, failed or was cancelled (API Standard 13.2 rule 3). |
system.lifecycle.maintenance-window | A maintenance window began (module schedules). |
system.lifecycle.maintenance-ended | Maintenance ended by itself at maintenanceUntil. |
component.failed | A component failed or stopped answering. |
component.recovered | A failed component works again. |
component.restarted | A component was restarted. |
ai.call.failed | A call to an AI model failed. |
ai.tier.changed | The way of thinking in use changed, such as from cloud to local. |
tending.autonomy.changed | A person changed the autonomy ceiling max. |
tending.silence.ended | A silence on an alert's code was ended early. |
tending.stopped | Stop was pressed: all autonomous action stopped. |
tending.action.failed | An Action ended failed. |
tending.guard.refused | A guard or interlock refused a command. |
tending.rule.proposed, tending.rule.suspended, tending.rule.failed, tending.rule.refused | A Playbook was proposed, suspended, failed when run, or was refused by the gate. |
tending.proposal.received | A brain's Proposal arrived. |
tending.trial.started, tending.trial.undoing, tending.trial.kept, tending.undo-failed | A Trial started, began its undo, was kept, or could not be undone. |
tending.envelope.exceeded | An automatic change would have gone outside its envelope, and was refused. |
circulation.received | A message from another system arrived. |
circulation.gateway.joined, circulation.gateway.unreachable | The system joined its Gateway, or cannot reach it. |
circulation.system.joined, circulation.system.left | At a Gateway: a system joined or left. |
security.model.invented, security.model.ungrounded | A model's answer named something that does not exist, or was not grounded in the system's own data, and was refused. |
More event types are added through the registry (Section 11).
6. Standard action names#
If a product offers one of these actions, it SHOULD use the name given here. Then one command, such as "restart that", works across a whole building or fleet of different products.
| Area | Names |
|---|---|
| Tending | retry_operation, restart_component, switch_provider, clear_cache, throttle, rollback |
| Devices | identify, calibrate, zero, self_test, sync_time, reset_counters |
| Actuators | start, halt, pause, resume, home, set_target |
| Data | flush_buffer, compact_storage, export_data |
| Connectivity | reconnect, rescan, switch_network |
halt stops a machine or one of its parts. It is not the HEART's Stop, which stops only automatic action and leaves the machine as it is (Part 6, Section 6).
Product profiles (Part 8) add names for their own kind of product.
7. Feature Sets: how much a system must say#
A Feature Set says how much of the information model a system offers. There are three. Each includes everything in the one before it.
| Feature Set | What it adds | Suits |
|---|---|---|
| Basic | Card, Pulse, Arrhythmias, the Valve | Every product, including small sensors |
| Connected | Rhythm history, questions in plain language, messages from other systems, subscriptions, Receipts, the full Card | Most apps and connected devices |
The full Card means the Card served at the caller's clearance (GET /heart/v1/card), and not only its public view (Part 4). | Full | Capabilities and Actions with approval, the stop control, AI model tracking, AI reasoning in the standard format | Systems that fix themselves or act on request |
A system states its Feature Set in its Card. If something is asked for that is above its Feature Set, it MUST answer "not implemented" (Part 4 says how).
Every Feature Set, even Basic, MUST also meet Autonomy Level A1 (Part 2). A system that only reports, without noticing problems and telling someone, is not a HEART.
Feature Sets and Autonomy Levels are different scales. The Feature Set says how much you can ask. The Autonomy Level says how much it does on its own. See Part 1, Section 5.6.
8. Modules: optional extras#
Feature Sets stay small, so a tiny sensor can take part. Everything else is an optional module. A system lists its modules in the Card's modules. It MUST offer all of a module, or none of it.
| Module | Adds | Typical user |
|---|---|---|
inventory | A tree of the system's parts, and what it relies on | Most systems |
properties | Named values to read or set, such as a temperature or a setpoint | Devices |
config | Versioned settings that can be checked before use, and rolled back | Most systems |
telemetry | Metrics and logs, for systems that do not use OpenTelemetry | Small devices |
diagnostics | Self-tests and support bundles | Devices, appliances |
lifecycle | Power and operating phases, such as sleep or maintenance | Devices |
software | Versions, signed updates and lists of included software | Most systems |
artifacts | Files and large data | Apps, printers, cameras |
schedules | Do something later, and maintenance windows | Most systems |
control | The stop button and Playbook rules. Required for Full. | Systems that act |
privacy | Valve settings, consent and personal data requests | Systems with personal data |
ai | AI providers, budgets and usage | Systems using AI |
brain | The brain lease and the switch: a Command Center can cut the AI model, and it is cut by itself when the lease runs out (API Standard 7.4). Required at A3 and above with a brain | Systems using AI to act |
memory | What the system remembers, and forgetting it on request | Systems using AI |
evolving | AI model versions, lessons and feedback | Systems using AI |
security | Keys, proof of identity, and audit records | Most systems |
platform | Hardware, power, network and clock | Devices |
provisioning | First setup, and changing owner | Devices |
peers | Which other systems it trusts | Systems that talk to each other |
batch | Several requests in one go, for slow links | Small or remote devices |
fleet | One command to many systems | Gateways |
custom | The product's own commands (Part 8) | Most devices |
native | Direct access to the device's own protocol. Off by default. | Engineers only |
core | The built-in HEART Core (Part 7) | Systems with a Core |
Two of the most useful module messages:
- Component (
inventory): one part of the system, with anid, akind(Section 4), its ownstatus, and optionally itsparent,version, maker and what it depends on. - Property (
properties): one value, with itsname,value,unit(UCUM), whether it iswritable, aqualityofgood,uncertainorbad(the three severities of OPC UA's StatusCode), thetimeit was measured, and itsprivacylevel. Thevaluetakes the level given inprivacy; this is the only box whose level is set per message (its schema marks itdynamic).
9. Naming rules#
Names are how systems find each other and avoid clashes. These rules keep them unique and readable.
| What | Rule | Example |
|---|---|---|
| System ID | A reversed domain name the owner controls, then the product name | com.example.cool-air.ac-controller |
| Instance | Which copy of the system. MUST NOT reveal who the user or owner is. | unit-12, browser-7f2c |
| Standard message types | Start with heart. (reserved) | heart.pulse |
| Product message types and fault codes | Start with the maker's reversed domain name | com.coolair.service-report |
| Capability names | Up to 128 letters, digits, _, . or - (the same as MCP tool names) | calibrate |
| Event types | Dotted lower-case words (Section 5.8) | wash.finished |
| Fault codes | Section 5 | sensors.drift |
Using a reversed domain name works the way Java packages and Android apps do. Only the owner of coolair.com can create names starting com.coolair, so names never clash.
10. Rules for change#
Products will mix HEART versions for years. These rules make sure old and new keep understanding each other.
- Ignore what you don't know. A consumer MUST ignore boxes it does not recognise. It MUST NOT fail.
- Missing means "not available to you". A missing box never means zero, false or empty. It may have been filtered out for privacy.
- Versions 1.x only add. A minor version may add boxes, words and codes. It MUST NOT remove anything or change what something means. Removing or changing needs version 2.0.
- Codes are forever. A fault code, its H-number, an action name or an event type, once published, never changes meaning and is never reused. It can only be marked deprecated (no longer recommended).
- Extras go in
ext. A product's own extra boxes go insideext, under the maker's reversed domain name, so they never clash with future standard boxes. They are private unless the Card says otherwise.
"ext": { "com.coolair": { "filterHours": 412 } }
11. Registries: the shared lists#
A registry is an official, shared list that everyone uses, such as the list of country codes or of internet port numbers. HEART keeps these registries:
| Registry | Holds | Section |
|---|---|---|
| Fault codes | Standard fault codes and their H-numbers | 5.4, 5.7 |
| Action names | Standard capability names | 6 |
| Areas | The areas used by fault codes and Autonomy Levels | 5.2 |
| Profiles | Product profiles, and their prefixes for fault codes and names | Part 8 |
| Event types | Standard Rhythm event types | 5.8 |
| Message types | heart. message types | 3 |
| Modules | Module names | 8 |
How to add an entry#
Anyone may propose an entry. A proposal gives:
- the name,
- one sentence saying exactly what it means,
- an example from a real product,
- why no existing entry fits.
An entry is accepted when it is clear, does not overlap an existing entry, and is likely to be useful to more than one maker. Until HEART has a neutral home (see the Standards Family), the editor of this standard keeps the registries in this repository.
12. Carrying the model in other standards#
The model in this part is the meaning. Part 4 defines the main ways to carry it: HTTP and JSON, MQTT, local sockets and in-process use.
Other carriers can be added later without changing the model, as long as they follow one rule:
Same boxes, same meanings, same privacy. Any carrier MUST keep every box with the meaning given here, and MUST apply the Valve's filtering (Part 5) before anything leaves the system.
Planned carriers:
| Carrier | Why |
|---|---|
| CBOR (compact binary JSON) | For very small devices that cannot afford text messages |
| OPC UA | So factory machines can offer HEART inside the standard factories already use |
| Asset Administration Shell | So a HEART can be one section ("submodel") of a factory digital twin |
| OMA LwM2M | For small managed IoT devices |
Each needs work with the group that owns that standard.
13. Changes needed in other files#
| Change | Where |
|---|---|
A machine-readable copy of each other registry, checked by tools/validate.py | New registries/ folder |
| Rename "conformance level" to "Feature Set" in text | API Standard, Core Standard, paper. The Card box stays level until version 2.0. |
14. Open items#
| Item | Why it matters |
|---|---|
| Is the starting fault-code list right? | It should be checked against real fault logs from several kinds of product before 1.0 final. |
| Thresholds | Codes such as storage.nearly-full and performance.slow depend on limits each product sets. Profiles may set defaults. |
| Mapping to existing code systems | NAMUR NE 107 (industrial device status), OBD-II and similar code lists could map to HEART codes, so existing devices can join easily. |
| Translations | Codes stay in English. The meaning sentences and explanations need agreed translations. |
| CBOR profile | Needed before the smallest microcontrollers can take part. |