Skip to content
Protocol
happi/1.5
Transport
stdio · HTTP · WebSocket
Event types
10
New in 1.4
continuity
Simulated · no server required

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

Choose a preset · the last two are new in 1.4
ready
HAPPI event stream (NDJSON)generated in this page
// event stream will appear here
Shipped in 1.4 · continuity

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":{…}}

Fields of the continuity_ref object
FieldRequiredMeaning
sha256yes 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.
kindyes continuity-commit or continuity-verdict.
predecessor_continuityno The id of the prior continuity node — the backward chain link. null at genesis.
settlesverdicts The sha256 of the commitment this verdict settles. Required on a verdict.
model_versionsauthor 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.

Run it yourself
01

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"}}
02

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"}}
03

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.