The crate README says which draft is implemented, the readings taken where -22 leaves an encoding open, the carriage code points and the WAI_RECEIPT layout, what is not implemented yet, and the vector corpus's JSON view. The version moves to 0.3.0 because the public API changed; both lockfiles that record it follow. Co-Authored-By: Claude Opus 5.5 <[email protected]>
122 lines
13 KiB
Markdown
122 lines
13 KiB
Markdown
# Changelog
|
||
|
||
All notable changes to the Joule Code standard are recorded here. The format follows the spirit of Keep a Changelog; the standard versions independently of its siblings (tags `joule-code-vX.Y.Z`).
|
||
|
||
## [Unreleased]
|
||
|
||
### Added
|
||
|
||
- **§5.1 Grant-gated consultation (OPTIONAL)** — the JCP binding: tier as granted action (`l0`…`l4`), energy class as chained budget debit, denial as fallthrough, every consultation sealed as a chained JCR-1 receipt. Reference binding: the `joule-code-jcp` crate (`JcpGate::wrap` over any `Stage`); no `l4` in the chain → no remote frontier agent, structurally. Conformance gains class **C5** with a published vector file (`gated_cascade_vectors.json`, fixed fixture identities, deterministic end to end) run by the harness against the real jcp runtime alongside C1–C4.
|
||
|
||
- `joule-code-cascade` ships a reference **L0 (Cache)** stage: a content-addressed cache of resolved coding operations keyed by `(prior, intent)`. A hit closes the request at L0 (picojoule class) without consulting any higher tier — the first concrete cascade stage, making stop-at-first-close demonstrable in the reference library. A third party may use it or substitute its own.
|
||
- `joule-code-cascade` ships a generic **`FnStage`**: any tier (L1 Lawful, L2 Embed, L3 Model) can be expressed by supplying a resolver closure, so a consumer binds the JouleClaw agent schema — or any other runtime/model — to the cascade without this crate depending on it. (A test wires an L3 model stage behind an empty L0 and confirms it closes at L3.)
|
||
- `examples/l3-jouleclaw/` — a consumer **binding** showing JouleClaw's L3 (Model) tier wired into the cascade: a `jouleclaw_llm_cheap::LlmBackend` behind a `FnStage(L3)` that generates a `CodingOp` + joules-class receipt. **Three** backends share the slot: `EchoBackend` (model-free, for the unit tests); `JouleclawRuntimeBackend`, which runs a GGUF model **in-process on JouleClaw's own from-scratch tensor runtime** (`jouleclaw-runtime` over `jouleclaw-loader-gguf` and the reference/Apple kernel backends — no llama.cpp, no FFI); and `LlamaCompletionBackend`, which shells out to llama.cpp. The `run` binary generates with a local model (in-process runtime by default; `JOULE_CODE_L3_ENGINE=llama` for the shell-out), content-addresses the result as a `CodingOp`, and **meters host energy over the inference** for the receipt (honest provenance: `HwShunt` on RAPL/NVML, `Estimator` on Apple). Verified live: the llama.cpp path against a local 350M GGUF, and the in-process from-scratch runtime against `ternary-bonsai-1.7b-q2_0` (qwen3, Q2_0) — real text, a real `op_id`, the cascade closing at L3 (`tests/runtime_l3.rs`, opt-in via `--ignored`). Standalone workspace, so the standard's lib stays free of a JouleClaw dependency; `jouleclaw_dispatch::ModelBank` selects the cheapest capable backend in production.
|
||
- `joule-code-collab` (Research) lands the **federated-collaboration interface** (`Collab` trait: submit / committed / membership), a **single-node reference cluster** (`LocalCluster`), and the **BFT consensus core** (`LockstepCluster`): a multi-node *lockstep deterministic simulation with BFT supermajority commit* — an op commits only when ≥`2f+1` of `n=3f+1` honest nodes independently validate it, and committed ops are `op_id`-ordered so every honest node derives a byte-identical log. Tested: commits with honest agreement, tolerates `f` Byzantine nodes (safety), loses liveness only beyond `f`, rejects non-members despite quorum, deterministic round ordering. It also adds `protected` — **FROST threshold authorization** of protected operations: a `t`-of-`n` FROST (Ed25519) aggregate signature over an op's `op_id`, verifiable against the cluster's single group key (no member holds the whole key; fewer than `t` cannot authorize). Tested: a 2-of-3 threshold authorizes and binds exactly one op_id; below threshold cannot. And **MLS group keying** (RFC 9420, via `openmls`, `tests/mls_keying.rs`): the cluster members form an MLS group and derive a shared group key with no central server — verified that members agree on the exported secret, that adding a member advances the epoch, and that a member an epoch behind derives a different (forward-secret) key. (SPEC §7, `spec/collab.md`.)
|
||
- `examples/transport-moqt/` — the **CommunicationOS (MoQT) transport binding**, the last of the four federation layers. A consumer binding that carries a collaboration's committed (consensus-ordered) coding operations over [`inv-moq`](../../joulesperbit/crates/inv-moq) — CommunicationOS's MoQT (Media over QUIC Transport) protocol with its JWP receipt extension. The end-to-end test wires the whole stack in-process: `LockstepCluster` commits ops → each is published as a MoQT object → every object carries a real Ed25519/BLAKE3 **signed receipt** → a subscriber receives all ops, verifies the receipts against the publisher key, and recovers the original `SignedOp` intact; a second test shows a receipt-requiring subscriber rejecting an untrusted publisher's group (ownerless authenticity). Driven through `inv-moq`'s `MockFabric` (real signatures + BLAKE3 merkle receipts, no QUIC; the `quic` feature is the real-wire path behind the same trait surface). Standalone workspace, so the reference library stays free of a joulesperbit dependency. **The four federation layers — consensus, threshold authorization, group keying, transport — are now all implemented and tested.** (SPEC §7, `spec/collab.md`.)
|
||
|
||
### Changed
|
||
|
||
- **§6 / `spec/receipt.md` §2 — a vendor counter is a hardware reading only while it is demonstrably live.** A reading from a vendor energy counter (RAPL, NVML, Apple SMC/IOReport) MAY be tagged `HwShunt`, and supply `measured_uj`, only while the counter has been observed advancing under load for the domain the figure claims (for IOReport: the CPU energy rail itself, in at least 5 sample intervals, at least 4 of every 5, over at least 300 ms of CPU burn). A counter that is present but not live — frozen, or republished in lumps — MUST NOT produce either; the reading falls back to the estimator. A figure MUST NOT claim coverage of a domain its counter does not read. No wire or conformance-vector change.
|
||
|
||
### Changed — `joule-moq` 0.3.0: `draft-ietf-moq-transport-22`
|
||
|
||
`joule-moq` speaks `draft-ietf-moq-transport-22` (1 October 2026), negotiated by
|
||
the ALPN `moqt-22`, in place of its draft-08-style codec. No byte of the old wire
|
||
form survives: -22 changed the varint, the control streams, every message and the
|
||
object framing. The crate's version moves to 0.3.0 because its public API changes:
|
||
|
||
- `draft22`: the codec of -22, a module of its own selected through `draft::Draft`,
|
||
so another draft can be added beside it. Every control message and parameter,
|
||
Key-Value-Pairs, Properties, Setup Options, the Authorization Token, Request IDs
|
||
and request-stream rules, subgroup streams, datagrams, fetch streams and padding,
|
||
and the status and error codes. Every refusal is a typed `WireError` carrying the
|
||
draft's consequence; `MoqError::Wire` wraps it.
|
||
- `MOQT_DRAFT_VERSION` is `0xff000016`. `transport_quic::ALPN_MOQT` is `moqt-22`.
|
||
- `messages` re-exports -22's control messages. `SetupMessage`, `AnnounceMessage`,
|
||
`SubscribeMessage`, `SubscribeFilter` and the draft-08 `ControlMessage` are gone.
|
||
SUBSCRIBE's `max_mrl_layer` is an OBJECT_PROPERTY_FILTER on the `MRL_LAYER`
|
||
property (`messages::mrl_cap_filter`); `require_receipts` is the subscriber's own
|
||
policy, met by subscribing to a track's receipts track.
|
||
- `carriage` (new): the crate's objects ride -22 subgroup objects, with the object
|
||
receipt, the `WAI_RECEIPT` property of WAI `extensions/moq-streaming-format.md`
|
||
§5 and the layer fields as properties in an application range; a group's
|
||
receipts ride its receipts track (§4.1). `ReceiptBundle` moved here from `mock`,
|
||
which re-exports it.
|
||
- `session`: the draft by ALPN (`MoqSession::select_alpn`); SETUP carries options
|
||
and no version or role, and `build_setup` takes no label; Request IDs by end.
|
||
- `transport_quic`: a pair of unidirectional control streams opened by SETUP, a
|
||
request stream per request, subgroup, fetch and padding streams, and datagrams.
|
||
`ControlStream`, `write_object_to_uni`, `read_object_from_uni`,
|
||
`write_group_receipt_to_uni` and `read_group_receipt_from_uni` are replaced by
|
||
`QuicSession::{open_control, accept_control, open_request, accept_request,
|
||
open_subgroup, open_fetch, accept_data}`, `send_object`, `send_receipt_bundle`
|
||
and `DataReceiver`.
|
||
- `wire::encode_varint` and `decode_varint` are -22's integer; every `u64` encodes.
|
||
- `mock`: objects, receipt bundles and requests cross the -22 codec; the relay
|
||
reads an object's delivery timeout from its OBJECT_DELIVERY_TIMEOUT property.
|
||
- `tests/vectors/draft-22/`: a corpus of -22 vectors, good and negative, generated
|
||
by `tests/draft22_vectors.rs`, and an independent decoder that checks it in CI.
|
||
|
||
### Changed — `joule-moq` 0.2.0: WAI's labelled JWP forms, and a strict verifier
|
||
|
||
`joule-moq` reads and writes the forms of WAI's `extensions/jwp-receipts.md`
|
||
that sign an object's energy figure, its class and its origin, and a group
|
||
total's class. Every receipt in the legacy forms keeps its bytes and verifies as
|
||
before; a verifier that predates the labelled forms refuses them. The crate's
|
||
version moves to 0.2.0 because its public API changes:
|
||
|
||
- `ObjectReceipt` gains `energy_class: Option<Vec<u8>>` (the class in WAI
|
||
energy-measurement §2.7's signed encoding). Present, the object's leaf is
|
||
`BLAKE3("moq-jwp:merkle-leaf\x02" ‖ content_hash ‖ u64_be(joules_micro) ‖
|
||
class ‖ u32_be(len(origin)) ‖ origin)`; absent, the legacy leaf. New:
|
||
`ObjectReceipt::leaf`, `ObjectReceipt::check_label`,
|
||
`ReceiptBuilder::add_labelled_object`, `ReceiptBuilder::class_for`.
|
||
- `GroupReceipt` gains `energy_class`, `energy_coverage` and
|
||
`parent_receipt_hash`. With a class, the group is signed under
|
||
`moq-jwp:group-sign\x02`, which covers the class, the coverage and the
|
||
parent link. `energy_coverage` is written only when it is non-zero, and a
|
||
legacy group carrying a non-zero coverage is refused: its signature does not
|
||
cover one. New: `ReceiptBuilder::finalize_labelled`, `GroupReceipt::link_hash`
|
||
and `compose_group_class`, the rule a labelled group's class is composed by.
|
||
- `ReceiptVerifier::verify_group` returns a `VerifiedGroup` (the root with the
|
||
group's leaf count and coordinates) instead of the bare root, and
|
||
`verify_object` and `verify_payload` take `&VerifiedGroup`. An object is
|
||
accepted only at the position its proof walks: its coordinates are the
|
||
group's, `object_id` is its leaf index, the proof has the group's depth and
|
||
its direction bits are the index's.
|
||
- `MerkleProof::verify` is replaced by `MerkleProof::verify_leaf`, which takes
|
||
the leaf in its form and the group's leaf count, and
|
||
`MerkleProof::walks_its_position`.
|
||
- `verify_group` holds the key and the signature to WAI jwp-receipts §3.1
|
||
step 3's rule: the publisher key must be the canonical encoding of a point
|
||
not of small order (a `y` coordinate ≥ p is refused), `S` must be below L
|
||
(checked by the crate as well as by `verify_strict`), `R` must not be of
|
||
small order, and the cofactorless equation must hold.
|
||
- `JWP_MOQ_EXTENSION_VERSION` is 2.
|
||
- `tests/wai_jwp_corpus.rs` runs every leaf, group and object vector of
|
||
`wai/jwp-conformance/vectors.json` through the crate.
|
||
|
||
External JWP implementations need the same update to verify labelled leaves and
|
||
groups, and to refuse what this verifier refuses.
|
||
|
||
## [0.2.0] — draft
|
||
|
||
### Changed
|
||
|
||
- **Receipts now seal as JCR-1 envelopes.** The receipt's signed form is the family-shared JCR-1 wire format — canonical det-CBOR (RFC 8949 §4.2.1, length-first key order) inside a COSE_Sign1 envelope (RFC 9052, EdDSA `alg = -8`), as the `joule-code/1` profile. This replaces v0.1's bespoke `estimated_uj ‖ sequence ‖ nonce` preimage. A Joule Code receipt now verifies under the family's existing JCR-1 tooling and its cross-language (Python/Go) reference verifiers. (SPEC §6.1.)
|
||
- `joule-code-receipt` gains a `jcr1` module (canonical CBOR + COSE_Sign1, vendored from the JCR-1 reference, no JouleClaw dependency) and `Receipt::to_jcr1_wire` / `seal` / `verify_envelope`; the bespoke `sign`/`verify` are removed.
|
||
- Conformance C3 vectors regenerated as JCR-1 COSE_Sign1 envelopes; the harness verifies the envelope and that its payload is the receipt's canonical core. A byte-equality test pins the canonical encoder against a published JCR-1 vector.
|
||
|
||
## [0.1.0] — draft
|
||
|
||
Initial draft standard.
|
||
|
||
### Added
|
||
|
||
- `SPEC.md` — normative specification: the cascade requirement (L0–L4, stop-at-first-close), the receipt requirement (`measured + estimated`, `Provenance`), and the no-capture requirement.
|
||
- The coding-operation cargo: content-addressed (BLAKE3-256), signed (Ed25519, prior-state-bound) transitions over a source tree; deterministic CBOR canonical form.
|
||
- The receipt schema in Joule Wire Protocol form, energy-signed.
|
||
- The cascade interface and the agent-schema binding to JouleClaw; the L4 binding to remote frontier agents over ACP.
|
||
- The federated-collaboration binding, specified at interface level and classified Research.
|
||
- `joule-code-rs/` reference library: `joule-code-schema`, `joule-code-cargo`, `joule-code-receipt`, `joule-code-cascade`, `joule-code-conformance`.
|
||
- `conformance/v1/` reference vector pack (C1–C4), internally consistent pending regeneration against the reference library.
|