Edit it. Run it. Read the stream.
It needs no backend at all: the stream plays out inside the page, token by token,
in the order a real runtime emits them. If a HAPPI server answers the badge
above flips to live and the same envelope goes over the wire instead, and if it
stops answering the page drops back to simulation mid-run. A live server
sends the version it implements, which can lag the specification this page
documents; the badge then reports the version that actually arrived, taken
from the events themselves rather than from anything written here. Run the
continuity preset simulated and the browser content-addresses the
commitment itself — the sha256 it returns is the one the reference
runtime computes for the same bytes. Change one character of the premise and it
changes with you.
Demo workspace
Commit to the premise before you act on it
- 01idr records what was decided.
- 02context records what was believed.
- 03A continuity event records what was assumed — the load-bearing premise an agent is about to act on, named together with the observation that would refute it.
The event is not a terminator, and that is the whole design. Unlike
idr and context, which close a dispatch, a
continuity-commit is emitted before the dispatch whose
reasoning depends on it. A reader checking the stream can therefore see that the premise
and its falsifier predate the outcome. Reasoning recorded after the result can always be
fitted to it; reasoning recorded before it cannot. The ordering is the guarantee.
A commit body carries the premise — claim, falsifier,
deps, severity. A later continuity-verdict settles
the commitment it names, carrying verdict (CONGRUENT /
INCONGRUENT / UNVERIFIABLE), axis,
evidence and verifier. The runtime rejects an unrecognised
kind, and rejects a verdict that names no commitment — a settlement joined
to no premise proves nothing.
The wire shape is one line, like every other event:
{"v":"happi/1.5","id":…,"type":"continuity","ts":…,"continuity_ref":{…}}
| Field | Required | Meaning |
|---|---|---|
| sha256 | yes | Content address of the commitment body — canonical JSON, sorted keys, excluding the volatile id, ts and audit fields. Byte-identical to the context address algorithm, so one implementation serves both. |
| kind | yes | continuity-commit or continuity-verdict. |
| predecessor_continuity | no | The id of the prior continuity node — the backward chain link. null at genesis. |
| settles | verdicts | The sha256 of the commitment this verdict settles. Required on a verdict. |
| model_versions | author | Model identifiers that produced the record, for the audit chain.
Required of the author by the spec, not enforced by the runtime:
omit it and the event still emits, carrying [], exit 0.
Pass an array — an object is coerced to its keys, so
{"anthropic":"claude-opus-4-7"} is recorded as
["anthropic"] and the model name is silently discarded. |
The protocol carries the record, never the policy.
Dependency-contamination closure, settlement worklists and any braking behaviour built on
these records live in the harness — exactly the division idr and
context already keep.
Pre-register the premise
Before the work it governs, not after. args[0] is the commitment
body as a JSON string; the runtime content-addresses it and hands the address back.
$ echo '{"v":"happi/1.5","id":"commit-1","cmd":"continuity.append",
"args":["{\"claim\":\"The gateway accepts happi/1.0 envelopes unchanged\",\"deps\":[\"runtime@1.4.0\",\"gateway@2.3.1\"],\"falsifier\":\"A happi/1.0 envelope returns error.code=parse_error\",\"severity\":\"high\"}"],
"flags":{"kind":"continuity-commit","model_versions":["claude-sonnet-4-6"]}}' | bash happi.md run
{"v":"happi/1.5","id":"commit-1","type":"started","ts":0}
{"v":"happi/1.5","id":"commit-1","type":"continuity","ts":0,"continuity_ref":{"sha256":"sha256:33c4c546f5a02960bd25be1d7359b620e3717ba4479b6746f4c1aeb1b1d0357b","kind":"continuity-commit","predecessor_continuity":null,"settles":null,"model_versions":["claude-sonnet-4-6"]}}
{"v":"happi/1.5","id":"commit-1","type":"completed","ts":0,"usage":{"content_addr":"sha256:33c4c546f5a02960bd25be1d7359b620e3717ba4479b6746f4c1aeb1b1d0357b","kind":"continuity-commit"}}
Settle it, naming the commitment by address
flags.settles carries the hex step 01 returned — a real address,
not a placeholder — so the two events join into a chain anyone can walk. The
verdict gets a content address of its own, and could itself be settled later.
$ echo '{"v":"happi/1.5","id":"verdict-1","cmd":"continuity.append",
"args":["{\"axis\":\"back-compat\",\"evidence\":\"12/12 happi/1.0 envelopes dispatched, 0 parse_error\",\"verdict\":\"CONGRUENT\",\"verifier\":\"ci:happi-compat-suite\"}"],
"flags":{"kind":"continuity-verdict","settles":"sha256:33c4c546f5a02960bd25be1d7359b620e3717ba4479b6746f4c1aeb1b1d0357b","model_versions":["claude-sonnet-4-6"]}}' | bash happi.md run
{"v":"happi/1.5","id":"verdict-1","type":"started","ts":0}
{"v":"happi/1.5","id":"verdict-1","type":"continuity","ts":0,"continuity_ref":{"sha256":"sha256:aa935f6efef7954e2548defa20c65312a1a092f775876d9aba79c7d5a0c8c0e7","kind":"continuity-verdict","predecessor_continuity":null,"settles":"sha256:33c4c546f5a02960bd25be1d7359b620e3717ba4479b6746f4c1aeb1b1d0357b","model_versions":["claude-sonnet-4-6"]}}
{"v":"happi/1.5","id":"verdict-1","type":"completed","ts":0,"usage":{"content_addr":"sha256:aa935f6efef7954e2548defa20c65312a1a092f775876d9aba79c7d5a0c8c0e7","kind":"continuity-verdict"}}
Watch it fail closed
Drop settles and the runtime refuses to write the record at all. A
settlement joined to no premise proves nothing, so a malformed record is treated as
worse than no record: parse_error, and the process exits 1. The
same refusal covers a kind outside the two the spec names.
$ echo '{"v":"happi/1.5","id":"verdict-2","cmd":"continuity.append",
"args":["{\"verdict\":\"CONGRUENT\"}"],
"flags":{"kind":"continuity-verdict"}}' | bash happi.md run
{"v":"happi/1.5","id":"verdict-2","type":"started","ts":0}
{"v":"happi/1.5","id":"verdict-2","type":"error","ts":0,"code":"parse_error","message":"continuity.append: a continuity-verdict must set flags.settles to the content address of the commitment it settles"}
ts is milliseconds since the process started, so on commands this fast it
reads 0 on your machine too. The sha256 is not a clock: it is
computed from the body's bytes alone, which is why these two hex strings are the ones
you will get, in any language, on any machine, today or in ten years. Change one
character of the claim and the address changes with you — and step 02 no longer
settles anything.
How the protocol works
One envelope in
Every HAPPI call is a single JSON object with v, id, cmd, and optional args. Provider-agnostic by design.
NDJSON stream out
The runtime emits one JSON object per line. Each line is a complete, self-describing event — parse with any JSON library, no framing protocol needed.
Ten event types
started → delta (×N) → completed. Plus error, tool_call, tool_result, sub_request, and the three signed records: idr, context, and continuity.
Timestamps count up
The ts field is milliseconds since the request started. Use it to measure first-token latency, total stream duration, and per-token throughput.
Back-compatible by construction
The runtime accepts happi/1.0 through happi/1.5 envelopes unchanged and emits happi/1.5 — emitted events carry the runtime's version, never the envelope's. A consumer that does not recognise a newer event type forwards it inertly rather than rejecting it.