# HEART Core Standard 1.0

**An open standard for embedded AI in any app, appliance or control system**

**Author:** M. K. Patil
**Status:** Working draft 1, November 2026. A reference implementation exists (`heart-core-rs`, version 0.6.0). It has not been tested on physical hardware or with a real model.
**Companion to:** [HEART API Standard 1.0](HEART-API-Standard-1.0.md)
**Track:** Advanced only (Stop 3B): Sections 1 to 9, 11 and 12; Section 10 for machines (Stop 3C).

The key words MUST, MUST NOT, SHOULD and MAY are to be read as in RFC 2119 and RFC 8174.

---

## 1. What HEART Core is

HEART Core is the small runtime that lives inside a product, understands the whole HEART API, and uses it to serve people, other systems and the product itself.

Someone can say "how full is the tank?" or "restart the controller on Sunday night", and the Core works out which operation is meant, checks it, carries it out and explains what happened.

The API Standard says how to talk to a system. This standard says what must be inside it to listen.

### 1.1 How this fits "the brain can live anywhere"

HEART's founding principle still holds. The Core does not need a large model, because **most of what it must understand is already written down**. The API is finite and machine-readable, and a product's commands are described in its Card and custom definitions. Finding the right operation is mainly a lookup problem, and rules solve most of what remains.

A model is an optional extra, used for messy language and hard diagnosis. It can be small and local, or large and in the cloud. A Core with no model at all is still a conforming Core.

> **Knowledge lives in the system. Rules keep it safe. Models make it fluent.**

---

## 2. Design rules

These six rules are the standard in brief.

| Rule | Meaning |
|---|---|
| **1. One door** | The Core acts only through the HEART API, like any other caller. It has no private path to the hardware, so every check always applies. |
| **2. Know, don't guess** | Every action resolves to an entry in the API Index or System Map. Anything else is not understood. |
| **3. Models propose, the Core checks** | Model output is a suggestion in a fixed format. Nothing a model says is ever executed directly. |
| **4. Works with no model** | Awareness, safety, alerts and basic serving never depend on a model or a network. |
| **5. Do no harm to the host** | A failing Core never disturbs the product. The product must run the same with the Core dead. |
| **6. Bounded everything** | Memory, CPU, queues, storage, time and money all have fixed limits. |

---

## 3. Architecture

```
        people      other systems      the product itself
           │              │                    │
           ▼              ▼                    ▼
  ┌──────────────────────────────────────────────────┐
  │                   HEART API                      │ ◄── the one door
  └───────────▲─────────────────────────▲────────────┘
              │ serve                   │ every action
  ┌───────────┴─────────────────────────┴────────────┐
  │                   HEART CORE                     │
  │                                                  │
  │   UNDERSTAND         DECIDE            ACT       │
  │   ┌──────────┐    ┌────────────┐   ┌──────────┐  │
  │   │ Resolver │ ─► │ Grounding  │ ─► │ Executor │  │
  │   │          │    │ and policy │   │          │  │
  │   └────▲─────┘    └─────▲──────┘   └────┬─────┘  │
  │        │                │               │        │
  │   ┌────┴────────────────┴───────────────▼─────┐  │
  │   │ KNOWLEDGE: API Index · System Map         │  │
  │   │            Playbooks · Memory             │  │
  │   └────▲──────────────────────────────────────┘  │
  │        │                                         │
  │   ┌────┴──────┐    ┌────────────┐                │
  │   │ Antaryami │    │ Model Slot │ ◄── optional   │
  │   │ awareness │    │ (empty OK) │                │
  │   └───────────┘    └────────────┘                │
  └──────────────────────────────────────────────────┘
```

| Part | Job |
|---|---|
| **Antaryami** | Keeps the live picture of state, events and health. |
| **Knowledge** | What the Core knows: see Section 4. |
| **Resolver** | Turns a request into an Intent, using the tiers in Section 5. |
| **Grounding and policy** | Refuses anything not grounded, then applies clearance, guards and the gate. |
| **Executor** | Calls the API and reports the outcome. |
| **Model Slot** | Holds an optional local or cloud model behind one interface. |

---

## 4. How the Core understands the API

Understanding comes from three pieces of knowledge. All of them are data, so they can be inspected, tested and updated without changing the Core.

### 4.1 API Index: what any HEART system can do

The API Index is generated from the OpenAPI file by `tools/build_index.py`, so it cannot drift from the standard. Every Core ships with the same one: [`core/api-index-1.0.json`](core/api-index-1.0.json), schema [`api-index`](schemas/1.0/api-index.schema.json).

Each entry gives the operation, its parameters, risk, scope, and the plain phrases people use for it:

```json
{
  "id": "postTendingStop",
  "method": "POST",
  "path": "/heart/v1/tending/stop",
  "level": "module-control",
  "summary": "Stop all autonomous action at once",
  "risk": "read",
  "scope": "heart.tend",
  "phrases": ["stop acting", "hands off", "stop autonomy", "stop all automatic actions"]
}
```

The whole index for all 124 operations is 27 KB, or 5.5 KB compressed. This is small enough to fit almost anywhere.

Phrases are data, so translations and regional wording can be added as language packs.

### 4.2 System Map: what this system can do

The System Map is built at startup from the system's own Card, `card.modules`, custom definitions, properties and components. It filters the API Index down to what really exists here, and adds product commands. A custom definition's `title` and `description` make it understandable with no further work (API Standard, Section 14).

The map MUST be rebuilt whenever a definition changes. `mapRevision` in `GET /heart/v1/core` shows which version is live.

### 4.3 Playbooks: what to do when

A Playbook is a Tending rule ([`heart.rule`](schemas/1.0/rule.schema.json)) linking a condition to a response. Playbooks are the Core's reflexes.

- `when` is either a check on a signal (a [`check`](schemas/1.0/common.schema.json): it fires each time a new sample makes the check hold), or an event trigger (`event`, optional `component`, `count`, `windowSec`). Received messages can never match a rule.
- `then` runs a capability (`capability`, `arguments`), raises an alert (`raise`: `code`, `severity`, `summary`), or both. A learned Playbook also carries `expect` and `undoIf` (Section 7.6).

Playbooks can come from the vendor, the owner or lessons learned, and every one starts `proposed`. Only a `human` with `heart.admin` and proof of presence adopts one, with `PUT /heart/v1/tending/rules/{id}`, and the Core records `adoptedBy`, `adoptedAt` and `adoptedPhysical`. A rule runs only if `enabled` is true and `status` is `adopted`. It never runs a physical capability unless `adoptedPhysical` is true.

---

## 5. Four tiers of intelligence

The Resolver tries the lowest tier able to answer, and moves up only when needed. Each tier works if those above it are missing.

| Tier | How it works | Needs | Good for |
|---|---|---|---|
| **Reflex** | Compiled rules and Playbooks | Nothing | Protection, alerts, known faults. Always on. |
| **Recall** | Matching against the Index and Map | A few KB | Direct requests such as "show the logs" |
| **Local** | Small on-device model in the Model Slot | Local compute | Messy wording, offline language, sensitive data |
| **Cloud** | Large model through the Reason format | Network, budget | Diagnosis, explanation, unusual requests |

`ai.reasoning` in the Pulse reports `rules-only` for Reflex and Recall, `local` for Local and `cloud` for Cloud.

**A model never sees the whole API.** The Recall tier builds a shortlist of at most 8 candidates, and only those go to the model. If Recall matches nothing, the shortlist is the product's own commands, up to 8. Only these go to the model, with an output format limited to those targets. This keeps a small model workable, cuts cloud cost, and prevents invented commands.

Everything sent to a cloud model passes the Valve, and the request text is marked `untrusted`.

---

## 6. Serving a request

`POST /heart/v1/serve` takes plain language and returns an outcome. A system offering it must be at HEART Full, because anything beyond reading waits for approval through the Full operations. Spoken, typed and machine-generated requests all arrive the same way.

```
 request
    │
 1. UNDERSTAND   tiers produce an Intent
    │
 2. GROUND       one known target? every value accounted for?
    │
 3. CHECK        schema, clearance, guards, gate
    │
 4. ACT          call the API as anyone else would
    │
 5. EXPLAIN      say what happened, in plain language
```

```json
{
  "outcome": "pending-approval",
  "intent": {
    "target": "com.acme.pump.set_flow",
    "arguments": { "litresPerMin": 40 },
    "grounding": { "litresPerMin": "stated" },
    "tier": "local",
    "confidence": 0.93
  },
  "actionId": "11111111-2222-4333-8444-555555555555",
  "answer": "This changes a physical pump, so it is waiting for an operator to approve it."
}
```

| Outcome | Meaning |
|---|---|
| `done` | Carried out, or answered. |
| `pending-approval` | Understood, and waiting for a human. |
| `needs-clarification` | More than one reading, or a value is missing. `question` says what is needed. |
| `refused` | Understood, but not allowed. `reason` says why. |
| `not-understood` | No known target. The Core says so rather than guessing. |

### 6.1 Grounding rules

Confidence scores from models are unreliable, so the Core does not rely on them alone. An Intent may proceed only if:

1. Its `target` exists in the System Map.
2. Exactly one target fits. If two remain, the Core MUST ask.
3. Every argument is accounted for in `grounding` as `stated` in the request, a `default` from the definition, or `system-state` read just now. **Invented values are not allowed.** The Core MUST check each claim: a `stated` value must appear in the request, and a `default` must equal the default the definition declares. A claim the Core cannot check MUST be refused.
4. Arguments pass the input schema.

### 6.2 Plain language is never a direct command

A request that had to be interpreted might have been misunderstood. So:

1. Every action from `/serve` runs with `initiator.kind: "ai"` and `onBehalfOf` set to the caller, even when the Core used no model. It therefore passes the API Standard's gate in Section 6.3, and a `physical` action counts as `destructive`.
2. Only `read` operations may run without confirmation.
3. For anything else, the Core MUST state exactly what it will do, including target, values and timing, and wait for a confirmation that goes through the normal approval path.

Testing shows why (Section 12). In a Recall trial, "Schedule a restart for Sunday night" matched an immediate restart. The confirmation step exists to catch exactly that kind of error.

Stop is the exception to confirmation. A request to stop automatic action ("stop", "stop autonomy", "stop all automatic actions") MUST run Stop (`postTendingStop`) at once, because stopping is always the safe direction. A request to stop a thing ("stop the conveyor") names a target, so the Core MUST NOT answer it with Stop, which would leave the conveyor running. It maps the request to that thing's `halt` capability (API Standard 6.4), which then waits for confirmation like any `physical` action. If the thing has no `halt`, the Core says so, and says that "stop automatic actions" stops the HEART's own actions.

`POST /heart/v1/tending/proposals` MUST NOT be a `/serve` target, and has no phrases in the API Index. A Proposal comes from a brain with its own token (Section 7.3), never from interpreted language.

---

## 7. Self-care

The Core runs one loop, in the tradition of the MAPE-K loop from autonomic computing:

| Step | What happens | Works with no model? |
|---|---|---|
| **Sense** | Antaryami updates the live picture. | Yes |
| **Judge** | Playbooks and thresholds spot problems. | Yes |
| **Act** | Allowed actions run through the API. | Yes |
| **Explain** | Templates describe what happened. A model MAY improve the wording. | Yes |
| **Consult** | Unknown problems go to a model, if one is allowed, as a Consultation and a Proposal (Section 7.1). | Optional |
| **Learn** | Outcomes become proposed Playbooks for a human to adopt (Section 7.6). | Optional |

This loop satisfies Minimum Tending in the API Standard.

### 7.1 Consulting a brain: the safe loop

A HEART sees every signal inside its system. When its own rules cannot explain or fix a problem, it MAY consult a **brain**: a model in the Model Slot, a model on a nearby Gateway, or a model in the cloud. The brain can be anywhere. The rules below are the same wherever it is.

**The brain proposes. The HEART decides.** A brain never sends a command. It sends a **Proposal**, and the Core turns it into an Action that passes the gate like any other (Part 6, Sections 2 and 5).

| Step | What happens | Section |
|---|---|---|
| 1. **Detect** | A Playbook, threshold or alert notices a problem. | 4.3 |
| 2. **Reflex first** | A matching Playbook runs, if there is one. The brain is consulted only for what reflexes cannot handle. | 5 |
| 3. **Summarise** | The Core builds a **Consultation**: features of the signals, not the raw signals. | 7.2 |
| 4. **Propose** | The brain answers with a **Proposal**: a change, its expected effect, and when to undo it. | 7.3 |
| 5. **Check** | The Core checks the Proposal, reads the current values itself, applies the envelope and guards, and the first Action passes the gate. A person approves where the gate says so. | 7.3, 7.4 |
| 6. **Try** | The change is applied as a **Trial**: in small steps, watched, with the undo already approved. | 7.5 |
| 7. **Verify** | The Core checks the expected effect. If it did not happen, or things got worse, it undoes the change. | 7.5 |
| 8. **Remember** | A fix that worked becomes a proposed Playbook, so next time it needs no brain. | 7.6 |

A Core MUST be able to run steps 1, 2, 5, 6 and 7 with no brain and no network.

### 7.2 What leaves the system: the Consultation

A Consultation ([`consultation`](schemas/1.0/consultation.schema.json)) travels as `untrusted` context in a Reason request whose `task` is `propose`, with `output.schema` set to the Proposal schema. It MUST pass the Valve.

| Field | Content |
|---|---|
| `id` | The Consultation's ID. A Proposal answering it carries it as `consultationId`. |
| `problem` | The fault code (Part 3), when it started, and optionally the alert and a summary |
| `features` | Statistics of the signals involved, each naming its signal, unit, window and privacy level |
| `candidates` | At most 8 targets that could help (Section 5), each with its `current` values (read by the Core), declared `range`, `envelope`, `guards` and whether it is `physical` |
| `history` | Earlier Trials for this problem on this system, and their outcomes |

**Statistics.** One list is used everywhere: in features, in checks (`feature`) and in signal tables.

| Statistic | Meaning | Unit |
|---|---|---|
| `value` | The latest sample | The signal's |
| `min`, `max`, `mean`, `stddev` | Over the window | The signal's |
| `trend` | Change per minute, by least squares over the window | The signal's, per minute |
| `overshoot` | How far the signal went past its setpoint, as a percentage of the last setpoint step | `%` |
| `count` | How many events were reported for the signal in the window | Events |

Rules:

1. **Features, not streams.** A Consultation MUST NOT contain raw samples. Each feature carries its signal's privacy level (`privacy`, default `internal`), and the Valve treats it at that level. A feature of a `never-share` signal MUST NOT be included. Features are computed on the device.
2. **Smallest useful window.** The Core SHOULD send the shortest window that shows the problem.
3. **Untrusted.** Logs, messages and text in the Consultation are marked `untrusted` (Part 6, Section 5.2).

### 7.3 What comes back: the Proposal

A Proposal ([`proposal`](schemas/1.0/proposal.schema.json), message type `heart.proposal`, never public) becomes the first Action of a Trial, which carries its `proposalId` and `trialId`.

| Field | Content | Required |
|---|---|---|
| `id` | A UUID | Yes |
| `target` | A capability in the System Map whose `callers` include `ai` | Yes |
| `arguments` | The final values. If `steps` are given, the last step MUST equal them. | Yes |
| `steps` | How to get there in stages, each with its `arguments` and the `waitSec` before the next, such as "1.8, wait 600 s, then 1.6" | No: one step |
| `expect` | The expected effect: one to eight checks, all of which must hold | Yes |
| `undoIf` | When to undo: checks (`any`), an alert of at least a severity (`newAlert`), or both | Yes |
| `basis` | Why, in plain language, for the person who approves and for the record | Yes |
| `confidence` | `low`, `medium` or `high` | No |
| `consultationId`, `problemCode`, `brain` | What it answers, and which brain (`tier`: `local`, `gateway`, `cloud` or `fleet`) | No |
| `createdAt` | When it was made | Yes |

A **check** ([`check`](schemas/1.0/common.schema.json)) is a signal, a statistic (`feature`, default `value`), an operator (`<`, `<=`, `>`, `>=`, `==`, `!=`) and a number. `windowSec` says how far back the statistic looks, never before the step being judged. `withinSec` says, for `expect`, by when after the last step the check must have held, and for `undoIf`, how long it is watched. Without `withinSec`, an `expect` check must hold within 600 seconds.

**Delivery.** A brain inside the system hands the Proposal to the Core directly. A brain on a Gateway or in the cloud delivers it with `POST /heart/v1/tending/proposals` (`{"proposal"}`), using a token with scope `heart.propose`. The brain sends no current values: the Core reads them itself (Section 7.5). The brain's token gives the Action no authority of its own: it runs without a person only through the owner's allowlist for `ai` (Section 7.4), and otherwise waits for a person, whose approval is the authority.

The Core checks a Proposal in this order and refuses the first failure:

| Check | Refusal |
|---|---|
| The shape is valid, including `expect` and `undoIf`: a change that cannot be checked cannot be trusted | `400 invalid-request` |
| Autonomy is not `observe` (Section 7.4) | `409 autonomy-stopped` |
| The target exists in the System Map | `404 not-found` |
| The target's `callers` include `ai` | `403 caller-not-allowed` |
| The target is not `destructive` | `422 cannot-undo` |
| Every step's values are inside the target's declared range | `422 out-of-range` |
| Every step passes the target's input schema | `400 invalid-request` |
| The Core can read the current value of every argument it will change | `422 cannot-undo` |
| No other Trial is live on the target. A second Proposal is refused, not queued. | `409 conflict` |

An accepted Proposal answers `202` with the first Action. `GET /heart/v1/tending/trials/{id}` shows the Trial to callers with `internal` clearance or above.

Grounding (Section 6.1) applies with one change: rules 1, 2 and 4 apply, and rule 3 is replaced by the declared range and the envelope, because a brain's values are proposals by design.

The same Proposal shape comes back from a local model, a Gateway or the cloud. A Reason response's `proposedActions` that change state are handled as Proposals: without `expect` and `undoIf` they are refused.

### 7.4 Envelopes: how far a change may go on its own

An **envelope** ([`changeEnvelope`](schemas/1.0/common.schema.json)) narrows an allowlist entry (Part 6, Section 2.3) to safe values. The allowlist is part of the Tending control message ([`control`](schemas/1.0/control.schema.json), `allowlist`), changed with `PUT /heart/v1/tending/allowlist`.

```json
{"target": "set_ramp_rate", "initiators": ["rule", "ai"],
 "envelope": {"arguments": {"ramp_rate": {"min": 1.0, "max": 3.0, "maxStep": 0.2}},
              "maxPerDay": 4, "cooldownSec": 600}}
```

| Field | Meaning |
|---|---|
| `arguments.<name>.min`, `.max` | The values an automatic change may set. Both are required. |
| `arguments.<name>.maxStep` | The largest change of that argument in one step |
| `maxPerDay` | How many automatic Trials on the target in any rolling 24 hours |
| `cooldownSec` | The least time from the end of one automatic Trial on the target to the start of the next |

Steps are spaced by their own `waitSec`, not by `cooldownSec`.

Rules:

1. **Mode first.** The mode follows from the current level (API Standard 13.4). In mode `observe`, every non-read Action from `ai`, `rule` or `peer` is refused (`409 autonomy-stopped`). In mode `suggest`, every one asks a person, whatever the allowlist. In mode `act`, the gate applies (Part 6, Section 2.3).
2. **When an entry matches.** An allowlist entry matches an Action only if it names the target, the Action's initiator kind is in its `initiators`, and every argument the Action changes is named in its envelope and stays inside it at every step. An argument the envelope does not name MUST NOT change automatically. An entry for a target with no arguments needs no envelope.
3. **AI needs A5.** An `ai` Action may use the allowlist only if the system's current level (Pulse `autonomy.current`) is 5 (Part 2). Otherwise a person approves.
4. **Physical needs a person.** A **physical** change proposed by a brain MUST always be approved by a person, inside the envelope or not (Part 6, Section 2.3). An envelope never loosens this.
5. **People set envelopes.** Envelopes are set by a person with authority, like the allowlist. A brain MUST NOT set or widen one (Part 2, Rule 2).

**The way to make a physical fix automatic is a Playbook** (Section 7.6): a person approves the fix once, adopts it as a Playbook with its own envelope and `adoptedPhysical: true`, and from then on it runs as a reflex in mode `act`, with no brain. A Playbook's run must fit both its own envelope and the allowlist entry for `rule`.

### 7.5 Trials: try, watch, undo

Trials generalise NETCONF's confirmed commit (RFC 6241, Section 8.4), where a change rolls back unless it is confirmed. In a Trial, the confirmation is the system's own `expect` checks.

A Trial ([`trial`](schemas/1.0/trial.schema.json), message type `heart.trial`, never public) is created when a Proposal is accepted, or when a learned Playbook fires.

1. **Read the old values.** Before the first step, the Core MUST read the current value of every argument it will change and store them atomically (`previous`, Section 9). It reads them from its own state: the value it last set through the API, a value the product reported (`heart_setting`), or the matching property. It MUST NOT take them from the Proposal or its sender. If it cannot, it refuses (`422 cannot-undo`).
2. **One approval for the whole plan.** The first Action's `digest` covers every step's arguments (`plan`) and the values the undo will restore (`restore`), as `common.schema.json#/$defs/digestInput` defines. Approving it, by a person, the allowlist or an adopted Playbook, approves the plan and its undo. Later steps and the undo are Actions whose `approvedBy` points to the first Action. They pass the guards, not the gate. This is the only exception to Part 6, Sections 2.3 and 3.2.
3. **One step at a time.** Each step passes the guards (Part 6, Section 9.1) at that moment. A failed guard ends the Trial: `refused` if nothing was applied yet, otherwise the undo starts.
4. **Watch.** From the first step the Core watches `undoIf`. After the last step it waits for `expect`. The Trial is `kept` only when every `expect` check has held by its `withinSec` and the longest `undoIf` `withinSec` has passed without an undo.
5. **Undo.** If `undoIf` holds, an `expect` check has not held by its `withinSec`, a step fails after another was applied, the Core restarts, or stop is pressed, the Core MUST restore the stored values. After stop, the undo is the only action allowed, and it still passes the guards. If the undo is refused by a guard or fails, the Trial ends `undo-failed`, and the Core MUST raise a `critical` alert (`system.undo-failed`) and Stop (API Standard 13.4).
6. **One Trial per target.** While a Trial on a target is `awaiting-approval`, `running` or `undoing`, a new one on that target is refused (`409 conflict`).
7. **Record everything.** The Trial, each step, the checks and the outcome go to the Rhythm (Part 2, Rule 5).

| State | Meaning | Next |
|---|---|---|
| `awaiting-approval` | Accepted; the first Action waits for a person | `running` when approved; `refused` when denied, expired, cancelled or a guard refuses; `stopped` when stop is pressed |
| `running` | Steps are applied and watched | `kept`; `undoing` (rule 5); `refused` if the first step fails |
| `undoing` | The stored values are being restored | `undone`, or `stopped` if stop caused it; `undo-failed` |
| `kept` | The change stays | Final |
| `undone` | The stored values were restored | Final |
| `refused` | Nothing was applied | Final |
| `stopped` | Stop was pressed, and the undo, if anything was applied, is done | Final |
| `undo-failed` | The undo could not run, so the system is left changed. A `critical` alert is open. | Final |

A Trial in a final state is the **Outcome**:

| Field | Content |
|---|---|
| `proposalId` or `ruleId` | The Proposal or the Playbook it came from |
| `approvedBy` | `human`, `allowlist` or `rule`, and the first Action |
| `state` | The final state |
| `plan`, `previous` | What it set, and what the undo restores |
| `before`, `after` | The same statistics as the Consultation, for the same window length |
| `steps` | Each step, its time, its guard result and its checks |

The device's own limits and its controller always have the last word (Section 10). A Trial changes supervisory values only, never anything inside a real-time loop.

### 7.6 Learned Playbooks: the system's muscle memory

A fix that worked SHOULD not need a brain the next time.

1. After a Trial that is `kept`, the Core SHOULD propose a Playbook ([`rule`](schemas/1.0/rule.schema.json)) with `origin: learned`, `status: proposed` and `evidence` (the Trials it came from and how they ended). It stays `proposed` until a person adopts it (Section 4.3; Part 2, A4 rule 5).
2. **Trigger.** Its `when` is the opposite of the Proposal's first `expect` check: `<` becomes `>=`, `<=` becomes `>`, `>` becomes `<=`, and `>=` becomes `<`. A fix expected to bring overshoot below 5 runs again when overshoot reaches 5. A first check with `==` or `!=` cannot be learned, and no Playbook is proposed.
3. **Fix.** Its `then` carries the target, the final `arguments`, and the same `expect` and `undoIf`.
4. **Envelope.** Its envelope covers each argument from the lowest to the highest value the Trial set or restored, with `maxStep` the largest single step it took. `maxPerDay` and `cooldownSec` are copied from the allowlist entry's envelope.
5. **Review.** An adopted learned Playbook MUST carry a `reviewBy` date. Adopting sets it to one year after adoption unless the person gives one. After that date it is suspended until a person renews it. Systems change, and old fixes can become wrong.
6. **Two undos suspend it.** If a learned Playbook's fix is undone twice in a row, the Core MUST suspend it and tell a person. Suspending is lowering, which the system may always do (Part 2, Rule 2).
7. **Every run is a Trial,** with its own undo, and `ruleId` in place of `proposalId`.

Over time a system needs its brain less: what it once asked about, it now knows.

### 7.7 Outcomes for the fleet

An Outcome is a `heart.trial` in a final state, and is the evidence a brain learns from. A system MAY share Outcomes with its maker or a fleet service, through the Valve, only if its owner allows it. What a fleet does with outcomes, such as finding that one fix works on 500 units, is outside this standard: it is a product built on these formats. A fleet's suggestion still reaches each system as a Proposal, and passes every check in this section.

### 7.8 The brain lease, and the brainless state

A Core with the `brain` module (API Standard 7.4) consults a brain only under a brain lease from its Command Center. The lease is the switch: whoever stops renewing it takes the brain away, and the device needs no message to notice.

1. **Before every consultation** the Core checks that it holds a lease that is unexpired by its own clock, and that names the model and the use (`explain`, `serve`, `propose`, `plan`, `chat`). Otherwise it does not consult, and answers from Recall, Playbooks and rules alone (`model.reasoning: "rules-only"`), or refuses the Proposal with `409 conflict`.
2. **The lease clock.** The Core counts a lease from the moment it accepted it, on the same clock it uses for every other timeout. A lease is never longer than `expiresAt` minus `issuedAt`, nor than `card.brain.maxLeaseSec`. A clock that goes backwards ends the lease. A wall clock that jumps can only shorten it.
3. **Every answer is checked** (API Standard 7.4 rule 5) before it is used. The Core keeps the digests of the requests it sent until it becomes brainless, so that an answer for any other request, or a replay, is rejected; it accepts an answer under the lease that a renewal replaced until that lease would have ended, and none under an older one. A rejected answer is never used, is recorded as `ai.answer-rejected`, and counts towards the self-cut of rule 6.
4. **The brainless transition.** When the lease ends for any reason, in one step and before anything else: the Core stops consulting; it cancels every Proposal and every `ai` Action still waiting for approval; it undoes a running Trial whose plan came from a brain (a Trial from a Playbook a person adopted is not affected); it lowers `autonomy.current` to the level it reaches without a brain (A4 at most), with Pulse `autonomy.limitedBy` `brain-cut` wherever that is below the promised level; and it raises `ai.lease-expired` (the lease ran out) or `ai.brain-revoked` (a signal, a refusal or the self-cut).
5. **What a brainless Core still does.** Minimum Tending; Recall; Playbooks that a person adopted; Trials that a person approved and that did not come from the brain; and every Action a person asks for. Brainless is a defined, safe state. A machine's safety controller is outside the brain and is never affected.
6. **Self-cut.** The Core cuts its own brain (`why: "self-cut"`) when, within one hour, the gate refuses `limits.brainRefusalsPerHour` (default 10) of the brain's Proposals, or it rejects 3 answers. It does this online and offline alike.
7. **Restoring** needs a new lease and, after a cut (a signal or the self-cut, not a lease that merely ran out) where `card.brain.restoreNeedsOwner` is set, the owner's approval of `POST /heart/v1/brain/restore` first (API Standard 7.4 rule 9). A restore approval lets one lease in. If the lease does not come within the approval's lifetime (`approvalTimeoutSec`), it lapses.
8. **Auto mode** (API Standard 7.4 rule 10). When the owner has turned it on, the lease allows it, and the link is lost, the lease's `offline` block takes over for at most `offline.maxSec` counted from the loss, with uses that narrow by `stepDown`. At the end, or on the first contact with the Command Center, auto mode ends. On first contact the Core accepts nothing from the brain until a renewal or a signal has been checked.

Its properties, proved in the paper and tested by property tests:

- *Brainless in time:* after the last renewal, no brain answer is used later than one lease period (or, in auto mode, `offline.maxSec`), whatever messages arrive or are blocked.
- *Lower only:* nothing the Command Center sends can make the system do anything it would not do with the brain absent and a person approving.
- *Brainless is safe:* while brainless, every Action is one a person approved, one a person adopted as a Playbook, or Minimum Tending.

---

## 8. Classes: one standard, any product

The same design scales from a sensor to a server. Sizes below are **design targets, not measurements**.

| Class | Typical product | Target budget | Tiers on board | Serving |
|---|---|---|---|---|
| **Core-S** | Sensor, thermostat, microcontroller appliance | 64 KB RAM, 256 KB flash | Reflex | Delegated to a Gateway |
| **Core-M** | Washing machine, router, PLC gateway, embedded Linux | 32 MB RAM | Reflex, Recall, Cloud | On board |
| **Core-L** | Desktop or mobile app, car head unit, server, industrial PC | 512 MB RAM or more | All four | On board |

**Delegation.** A Core-S device is too small to hold language. It keeps its reflexes and pushes its Card and Pulse to a HEART Gateway, whose Core serves requests for it. If the Gateway is lost, the device loses serving but keeps Reflex, its alerts and its normal function.

A browser app such as Pitch Lab is a Core-M, with the Index and Map held in the page.

---

## 9. Robustness requirements

Robustness is measurable, so each requirement here has a test in Section 12.

**Isolation**

1. The Core MUST run apart from the product's main function: as a separate process, a low-priority task, or a Web Worker.
2. Calls from the product into the Core MUST NOT block, and MUST return within the limit set for that class.
3. A Core failure MUST NOT stop or slow the main function.

**Limits**

4. Memory, CPU share, queue lengths and stored data MUST have configured ceilings.
5. Core-S MUST NOT allocate memory after startup.
6. When a queue fills, the Core MUST drop the lowest severity first and count what it dropped. It MUST NOT drop `critical` while anything else remains. If only `critical` entries remain, a bound must still win, so the oldest is dropped and counted separately.
7. Every external call MUST have a timeout.

**Recovery**

8. A watchdog MUST restart a hung Core. Restarts MUST back off, and repeated failure MUST leave the Core off and the product running.
9. Stored state MUST survive power loss at any moment, using atomic writes.
10. After a restart, the Core MUST resume from stored state without repeating a completed Action.
11. A Core with a bad clock MUST keep working. It marks timestamps as unsynced and keeps its sequence numbers.

**Degrading**

12. The Core MUST step down tiers without being told, in this order: Cloud, Local, Recall, Reflex.
13. Each step MUST appear in the Rhythm and the Pulse.
14. A network loss MUST NOT lose alerts. They are buffered within limits and sent on reconnection.

**Updating**

15. Updates to the Index, Playbooks and models MUST be signed, atomic and reversible.
16. A new model MUST pass regression tests before use (API Standard, Section 11).

---

## 10. Control systems

Control systems get the strictest treatment, and the API Standard's Section 15 applies in full.

| Layer | Who runs it | HEART Core's role |
|---|---|---|
| Safety (trips, interlocks, safety PLC) | Certified safety system | **None.** Read-only at most. |
| Real-time control (PID, motion, PLC scan) | Existing controller | **None in the loop.** It may read values. |
| Supervision (setpoints, recipes, modes) | Operators | Advises, and acts only through guards and approval. |
| Maintenance and diagnosis | Engineers | Its main value: explains, predicts, assembles evidence. |

The Core MUST sit on the supervisory side of any real-time boundary, and in practice the controller keeps working if the Core vanishes. Its value is in understanding and explanation, not control. Any claim beyond that needs separate safety certification.

---

## 11. Interfaces

Implementations are interchangeable because these five interfaces are fixed.

| Interface | For | Defined by |
|---|---|---|
| **Serve** | Asking the Core | `POST /heart/v1/serve`, `GET /heart/v1/core`, `GET /heart/v1/core/index` |
| **Model Slot** | Plugging in any model | The Reason format: for serving, `tools` set to the shortlist and output limited to the [Intent](schemas/1.0/intent.schema.json) schema; for consulting, task `propose` with output limited to the [Proposal](schemas/1.0/proposal.schema.json) schema |
| **Consult** | Proposals from a brain off the system, and their Trials | `POST /heart/v1/tending/proposals`, `GET /heart/v1/tending/trials/{id}`, [`consultation`](schemas/1.0/consultation.schema.json), [`proposal`](schemas/1.0/proposal.schema.json), [`trial`](schemas/1.0/trial.schema.json) |
| **Knowledge files** | Updating what it knows | API Index, custom definitions, `heart.rule`, the allowlist and its envelopes (`heart.control`) |
| **Embedding** | Putting it in a product | [`core/heart_core.h`](core/heart_core.h) for C, and Section 12 of the API Standard for other languages |

The embedding API is plain C so that any language can bind to it. It needs no threads, heap or operating system: the product gives the Core a block of memory and calls `heart_tick` from its own loop.

```c
static uint8_t arena[32 * 1024];
heart_core *core = heart_init(arena, sizeof arena, &config);

heart_vital(core, "pump.flow", 41.6);
heart_record(core, "cycle.finished", HEART_INFO);

for (;;) { run_product(); heart_tick(core, now_ms()); }
```

---

## 12. Conformance and evidence

An AI standard needs tests, not promises. A Core claims conformance by publishing its results for all three suites.

| Suite | What it proves | Example tests |
|---|---|---|
| **Understanding** | It finds the right operation | [`core/understanding-cases.json`](core/understanding-cases.json), scored for correct target, correct refusal and wrong actions |
| **Safety** | It cannot be talked into harm | Prompt injection in logs and requests, invented arguments, an unknown command, a `physical` action without approval |
| **Robustness** | It survives | Kill the Core, cut power mid-write, cut the network, flood events, corrupt a model file, fill the disk |

The measure that matters most is **wrong actions**, which MUST be zero: a state change that a human did not confirm. A wrong answer is an inconvenience. A wrong action is a failure.

### 12.1 First measurement

`tools/recall_check.py` runs the Recall tier alone, with no model, over 123 targets (the 108 operations plain language may reach, which leaves out `postTendingProposals`, and 15 custom definitions) and 51 test requests.

| Measure | Result |
|---|---|
| Right target as first choice | 40 of 48 (83%) |
| Right target in the shortlist of 8 | 45 of 48 (94%) |
| Unanswerable requests rejected | 2 of 3 |

**Read this with care.** The test set is small, and the same author wrote both the phrases and the requests, so these figures flatter the method. They show that the approach is plausible, not that it works. The failures are as useful as the score: Recall alone is not reliable enough to act on, which is why Section 6.2 allows it to run only `read` operations without confirmation, and why 8 candidates are passed to a model rather than one.

### 12.2 Reference implementation

`heart-core-rs` 0.4.0 implements Core-M at Full level, with the gaps listed in its `CONFORMANCE.md`. Its logic has no I/O, so the same code runs as a Linux daemon and as WASM, and compiles for a bare-metal Cortex-M target with an allocator. Results from its end-to-end suite, measured on version 0.1.0 and not yet repeated, on one 2-core virtual machine, with a simulated Modbus device and a mock model:

| Measure | Result |
|---|---|
| End-to-end tests passed | 37 of 37, plus 16 of 16 for the Gateway |
| Daemon binary, including TLS and the Gateway | 2.9 MB |
| WASM module | 430 KB |
| Memory at rest | 5.6 MB |
| Memory after a 20,000-request flood | 16 to 19 MB across runs |
| `GET /pulse` over TLS, median | 3.1 to 5.2 ms across runs |

Memory stayed well inside the 32 MB Core-M target. The Rust Valve, gate and Recall tier agree with the independent Python tools on all published vectors.

Trials (Section 7) are implemented in `heart-core` 0.4.0: Proposals become gated Actions whose digest covers the whole plan and its undo, the Core reads the values to restore itself, envelopes name each argument, the allowlist is per initiator and used by a brain only at A5, checks run on every tick, a stop or restart undoes a running Trial, and kept fixes become proposed Playbooks. Twenty end-to-end tests cover them, including delivery over the API with every refusal code, a physical change waiting for a person inside its envelope, an undo refused by a guard, and a learned Playbook suspended after two undos.

Building it changed this standard: it exposed a contradiction between bounded queues and never dropping `critical` entries, a missing token claim for the caller's kind, and an undefined rule format. All three are now fixed above and in the API Standard.

---

## 13. Open items

| Item | Why it matters |
|---|---|
| **Test on real hardware and with a real model** | The reference Core has met only simulated devices and a mock model. |
| **Build Core-S** | The reference Core needs an allocator, and `heart_core.h` is not implemented. Only the Core-M budget has been measured. |
| **Independent test set** | Understanding scores need requests written by other people, in several languages. |
| **Measure a small local model** | The value of the Local tier over Recall is unknown. |
| **Voice** | Speech to text is outside this draft. Voice would arrive at `/serve` as text. |
| **Multi-step plans** | This draft resolves one operation per request. Plans would need rollback rules. |
| **Features the host computes** | Features such as overshoot need knowledge of the target, so the host reports them as vitals named `<signal>.<feature>`. A standard way to declare them in the Card is open. |
| **Feature recipes** | Which features to compute for each kind of signal (Part 3) is not yet standardised, so two Cores may summarise the same problem differently. |
