active-infinity 1.2.4

Active Infinity (Active ∞) — binary, zero-copy, content-addressable, graph-native, unit-aware and secret-safe data language for Active modules and AI agents
Documentation
# Changelog — Active Infinity

## 1.2.4 — 2026-10-27

Fourth review pass; no wire-format change. The verification round after these
fixes reported no new findings.

- **`intents()` matches the stored form.** It applies the same occurrence
  replacement the canonicalizer uses, so an in-memory module and its decoded
  image give the same answer. The projections' data-text rule (whole-value
  marker) is unchanged and now documented separately from the image's prose rule.
- **Identifier nodes follow the label rule.** `Inode::Ident` redacts by
  occurrence like every other name; string values keep the whole-value rule.
- **`provenance_pairs` is documented as a raw accessor**, so a caller knows to use
  `provenance_json`/`provenance_lines` for output that leaves the process.
- **Redaction edge cases are tested explicitly**: a secret equal to the marker, a
  secret that contains it, overlapping secrets, empty entries and no secrets; the
  property generator now reaches uppercase secrets, including `SECRET` and
  marker-containing ones.
- **The spec states where a provenance key collision is reported**: the encoder
  refuses it (`infinity/encode`) because the resulting image would carry
  duplicate keys, which a reader rejects (`infinity/provenance`).

## 1.2.3 — 2026-10-27

Third review pass. No wire-format change. Two of these are regressions introduced
by 1.2.2 and are the reason 1.2.2 is yanked.

- **Redaction is a fixpoint for every secret, including one that contains the
  marker.** A replacement can join the text around it into a new secret
  (`"hid" + marker + "den"`), so the pass now repeats until the text is clean and
  falls back to the marker, with matches applied longest-first so the result is
  independent of the secret table's order. Property-tested over random secrets
  and texts.
- **An empty module is valid again.** `InfinityModule::check_depth` returned
  `infinity/cycle` for a module with no nodes; it now returns `Ok`, and
  `check_depth(0)` still runs the cycle check.
- **Agent-facing labels are redacted.** `value_paths` and `node_label` returned
  raw binding and field names, so a name containing a secret copy could reach a
  log or a model. Keys are now redacted like every other label.
- **Provenance key collisions are refused, not silently merged.** When redaction
  maps two keys onto one, the encoder reports `infinity/encode`
  (`provenance keys collide after redaction`) instead of dropping an entry; an
  image with duplicate provenance keys was never valid.
- **Projections agree on duplicate provenance keys** (first entry wins) for a
  hand-built module that never went through the encoder.
- **Labels and data text follow one documented rule**: data text that contains a
  secret becomes the marker, labels have each occurrence replaced. Unit names and
  environment names are labels; `docs/spec/projection.md` states the rule and the
  reason structural names are not rewritten in the image.
- **Cycle reports agree** between the depth and acyclicity sweeps (lowest node
  that could not be ordered).

## 1.2.2 — 2026-10-27

Second review pass over the 1.2.1 fixes. No wire-format change; the fixes make
redaction a true fixpoint and close the last copy path.

- **Redaction is a fixpoint for every secret.** The 1.2.1 rule only avoided
  injecting the marker when a *matching* secret was itself part of the marker; a
  second secret could still introduce it later, so a 1–3-byte secret such as `S`
  changed the text on every pass. The rule is now decided once per text: if any
  secret occurs inside `***SECRET***`, a text that needs redaction collapses to
  the marker. `interop::redact_text` and the canonicalizer share one
  implementation, and `SECRET_MARKER` has one definition.
- **Provenance keys are redacted.** `redact_provenance` rewrote values but not
  keys, so a copy embedded in a provenance key stayed in the string heap. Keys are
  now redacted like values, and entries that collapse onto the same key are
  deduplicated (provenance is a map), so the encoder cannot emit duplicate keys.
- **Projection value and key rules are consistent.** Value positions
  (`intents`, provenance producer/model/tool/text entries, Protobuf text fields)
  use the whole-value marker; key positions use occurrence replacement. The spec
  says the same, and `docs/spec/projection.md` now also states that structural
  names (field, binding, env, identifier names, media types) are *not* rewritten
  in the image — they are identity — while projections redact them and may merge
  two keys.
- **`InfinityModule::check_depth(0)` still checks cycles.** Zero means "no depth
  ceiling", not "skip validation"; a direct caller no longer gets `Ok` for a
  cyclic module.
- **Documentation matches the code.** `infinity/cycle` is documented as "a node
  on a cycle or depending on one" (the Kahn sweep reports the lowest such node),
  and the same wording appears in the error message.

## 1.2.1 — 2026-10-27

Independent review of the 1.2.0 candidate found eight issues; all are fixed here.
No wire-format change: every previously valid image stays valid, and every
previously canonical image stays canonical.

- **The Protobuf projection is canonical.** It is built from the canonical module
  (the decode of its own image), so logically equal modules — same AINF bytes and
  hash — project to identical bytes, and bindings, annotations and extensions come
  out in canonical order. Before, a table built with fields in a different order
  produced a different projection.
- **A projection cannot copy a secret through an opaque payload.** `to_protobuf`
  refuses an extension whose payload contains a secret literal instead of emitting
  it (`infinity/encode`, reason `extension payload carries secret material`).
- **Validation is linear in edges, not quadratic.** Acyclicity and depth use a
  Kahn topological sweep, with one child-list derivation per node; a wide table no
  longer turns validation quadratic.
- **Extension payloads must be packed.** Each payload now has to start exactly at
  `align8(previous end)` and the padding between payloads must be zero, so a
  directory cannot hide slack. New conformance vector `bad_extension_gap`.
- **One canonical proof.** `DecodeOpts::verify_canonical` now proves canonicality
  unconditionally, exactly like `validate_canonical` (which no longer encodes
  twice). An unflagged image fails the proof, because the canonical encoding of
  its content carries the flag.
- **One redaction rule.** `redact_text` (annotations, provenance, intents) no
  longer has a four-byte threshold, matching the projections: a one- or two-byte
  secret is replaced wherever it appears.
- **Taint follows references.** An interpolation whose expression is a `Ref` (or
  `Semantic` wrapper) around a secret redacts the whole string instead of
  rendering `"prefix ${}"`.
- **Tests and fuzzing cover the projections.** The independent Protobuf reader now
  checks ascending field numbers and minimal varints; new tests cover
  `i64::MIN`/`i64::MAX`, NaN, `-0.0`, empty text, canonical-envelope equality,
  secret-bearing extensions and short secrets; the decoder fuzz target exercises
  both projections and asserts the projection is deterministic.

CI also tests the locked graph with `--locked`, builds every target and feature on
the declared MSRV (1.85) with and without default features, verifies the package
tarball, and fuzzes on pushes as well as pull requests.

## 1.2.0 — 2026-10-27

No wire-format change: images written by 1.1.x remain valid and canonical. This
release adds a binary interop projection, first-class extension blocks, and a
conformance suite that covers every byte-reachable failure class in the
specification.

- **Protobuf projection.** `to_protobuf` renders a module as Protobuf wire
  format for consumers in other languages: root value, bindings, annotations,
  provenance, extensions, and the canonical image's Blake3 hash and size. The
  encoding is deterministic and redacted, uses no new dependency, and is
  documented in `docs/spec/projection.md` and ADR-0009. An independent reader in
  `tests/protobuf_tests.rs` checks the bytes.
- **One redacted tree for both projections.** `interop::Projected` is built once
  and rendered to JSON or Protobuf, so the two projections cannot disagree about
  what is safe to expose. `to_redacted_json` output is unchanged.
- **Extension blocks are first-class in the API.** `InfinityModule::add_extension`,
  `extensions` and `set_extensions` carry application-defined `(tag, payload)`
  blocks; encoding sorts them by tag and refuses duplicates, decoding returns
  them, and the validator checks the directory, payload order and the section's
  end.
- **Acyclicity is its own validation step.** A cycle is reported as
  `infinity/cycle` after reachability instead of surfacing as a depth failure,
  so a peer can tell "too deep" from "cyclic". The check is iterative.
- **Duplicate table fields name their node.** A repeated field key reports
  `infinity/duplicate-field { node }`; an unsorted key still reports
  `infinity/node`.
- **Conformance vectors cover the specification.** 34 invalid images and 9
  invalid streams, one per byte-reachable failure class, plus a
  specification-to-vector test: a new error code fails the suite until it has a
  vector or an explicit, justified waiver.

## 1.1.3 — 2026-10-27

- **Begin respects the caller's ceilings.** The declared stream size is compared
  with the decoder's own max_total_bytes, not the defaults.
- **Text projections redact keys and intent text.** provenance_lines redacts
  entry keys and intents() redacts intent text against the module's secret
  literals, matching the JSON projection. New helpers: `secret_texts` and `redact_text`.

## 1.1.2 — 2026-10-27

- **Delta fragments honour the caller's limits.** A Delta fragment is decoded
  and validated under StreamDecoder's Limits, so a strict consumer cannot be
  handed a larger module through a delta than through a generation.
- **Projection redaction covers metadata.** Table keys, unit names, environment
  names, blob media types and every provenance text field are redacted against
  the module's secret literals, not only values; provenance_json takes the
  secret table and provenance_json_for(module) supplies it.

## 1.1.1 — 2026-10-27

Security and correctness fixes from the independent review. No wire-format
change: images written by 1.1.0 remain valid and canonical.

- **Streaming bounds.** A stream may no longer accumulate more section bytes than
  the size declared by `Begin` (or `Limits::max_total_bytes`), and pending delta
  operations are capped by `Limits::max_aux`. Both report `infinity/limit`. A run
  of batches could previously exhaust memory before `End`.
- **Stream decoder limits.** `StreamDecoder::with_limits` lets a caller apply its
  own `Limits` to the reassembled image; the decoder no longer silently uses the
  defaults.
- **Truncated `finish()`.** `StreamDecoder::finish` no longer indexes past a
  partial frame; it reports `infinity/truncated`.
- **Depth over every root.** The depth ceiling is measured from every root
  (module root, bindings, annotation subjects), not just node 0, so a second
  shallow root cannot hide a deep chain.
- **Total-byte and string-heap ceilings** are enforced before decoding.
- **Provenance is a map.** `set_provenance` keeps the last value per key, so the
  encoder can never emit duplicate keys that validation rejects. Reserved entry
  kinds (4+) are refused instead of decoded as a different kind.
- **Projection guards.** Redacted JSON refuses cyclic/dangling modules and bounds
  recursion depth; `value_paths` is cycle-safe.
- **Retired `DeltaOp::PushNode`.** The variant produced unreachable nodes and was
  dropped after canonicalization; tag 2 is refused. Use `PutNode`/`SetBinding`.

## 1.1.0 — 2026-10-27

First production release: canonical `AINF` codec, fail-closed validator,
zero-copy views, streaming frames and deltas, `active-core` bridge, agent graph
queries, redacted JSON projection, conformance vectors, fuzz target.