# Changelog

All notable changes to `bounded_authority_protocol` are documented here.

## [1.0.0] — 2026-11-10

### Changed

- `inspect/2` no longer prints credentials. `V1.Credentials` redacts both compacts;
  `V1.ExpectedRequest` redacts `cast_arguments` and a required nonce (`{:required, :redacted}`);
  the `Proof` input of every contract-major redacts `grant_compact`, `cast_arguments` and the
  nonce; every major's `DecodedProof` redacts the nonce; `V1.SigningInput` redacts its segments
  and message. The other fields still render, as `#Module<field: value, …>`. Facts structs keep
  their fixed `#Module<redacted>` form. Deliberately logging a field still logs it.
- `V1.Bounds` no longer enforces its keys: every field defaults to its profile maximum, so
  `%Bounds{}` equals `Bounds.maximum()` and `%Bounds{depth: 8}` tightens one ceiling. A bound added
  in a later minor release defaults to its maximum and leaves existing literals compiling.
  `coerce/1` still rejects widened, zero, negative, non-integer and fixed-width-changing values;
  no maximum changes. The module documents every field with its maximum and unit.
- The request-digest projection enforces its depth, node, container and string budgets while it
  walks `cast_arguments`, on every contract-major (`request_digest/3`, proof production and
  envelope verification all share it). Before, it projected the whole host-supplied tree and only
  then checked bounds, so a two-million-item argument cost a full walk; it now stops at the first
  exceeded budget. The accepted set and the digest bytes are unchanged (a property test compares
  it with the unbudgeted projection).
- `Conformance.Corpus`, `Conformance.Runner` and `Conformance.Report` are now internal
  (`@moduledoc false`) and outside the stable surface; `Conformance.Cli.run/1` and the
  `bounded_authority_conformance` escript are the supported way to run a corpus. The CLI now exits
  0 only when the executed cases are exactly the certified census: unique case ids, the same id set
  and count as the index, each case executed from the file that declares it. Before, a run that
  dropped a case (or dropped one and repeated another) still reported agreement. The report's
  members (`format`, `agreement`, `exit_status`, `total`, `agreed`, `disagreed`,
  `index_sha256_base64url`), the flags and the exit statuses are a stable CLI contract, documented
  on `Conformance.Cli`.
- The conformance CLI verifies the three sibling-profile corpora (local-loopback HTTP, role
  attestation, content assertion), recognized from their index like the other six, so every
  packaged corpus runs from an installed package. `mix conformance.verify` runs all nine.
- The conformance CLI reads `index.json` and then only the files it names, never following a
  symbolic link: it refuses a link or other non-regular entry inside the corpus directory, a file
  the index does not name, an unsafe index path, a file over 1 MiB and a directory over 4,096
  entries. `--help`/`-h` and `--version` print to standard output and exit 0 (`--help` used to
  exit 2). Usage errors go to standard error. Every failure writes one value-free line to standard
  error naming its class, and the report ends with a newline.
- The architecture gate pins the package 1.x stable surface (`stable_modules/0`), its exclusions
  (`excluded_surface/0`), the field set of every stable struct (`public_struct_fields/0`) and the
  `RoleAttestation.V1` exports; a test checks them against the compiled package.

### Documentation

- Every public input, decoded and facts struct has a `@typedoc` and a "Fields" section giving each
  field's meaning, unit, limit (the `Bounds` key that caps it) and normalization, and every
  `V1`, `V2`, `V3` and local-loopback façade function has a `@spec`. No behavior change.
- `V1.Jwk.thumbprint_raw/2`, `V1.Jwk.public_key_thumbprint_raw/2`, `V3.EcJwk.thumbprint_raw/2` and
  `V3.EcJwk.public_key_thumbprint_raw/2` are now documented public API (they were `@doc false`
  although the quickstart uses them), with executable examples. `V1.Jwk.thumbprint_digest_unchecked/1`
  stays undocumented and outside the stable surface.
- New guides: Choosing a profile; Recipes (Plug and Phoenix integration with tagged
  `cast_arguments`, selectors per contract-major, ES256 from KMS or HSM DER, issuer key rotation,
  nonce modes, running every packaged corpus through `Conformance.Cli.run/1`); Evidence;
  Troubleshooting `{:error, :invalid}`; API stability and versioning, checked against the
  architecture gate's stable surface; Other languages, which replaces the four SDK deployment
  pages on HexDocs; Glossary; FAQ; and a runnable cheatsheet. Upgrading is rewritten for 0.x to
  1.0, the README is a short landing page with a versions table, and the threat model covers
  ES256 and the sibling profiles. Every Elixir block in the guides runs in the test suite.
- Two new Livebooks, service integration and chains and archives; the walkthrough uses the
  `Es256` helpers and states each expected result. One harness runs every notebook against the
  checkout in `mix test` and against the unpacked package archive in `mix package.check`, and
  `scripts/livebook_registry_readback.exs` runs each notebook's own `Mix.install` against Hex
  after a release.
- HexDocs groups: Start here, Guides, Reference, Specifications, Conformance and governance, and
  Project; facade functions are grouped as Producers, Decoders, and Verifiers. A docs gate rejects
  decision-record citations in shipped files, front matter on HexDocs pages, and a bare
  "v1"/"v2"/"v3" in guide prose.

### Fixed

- Erratum 6, a soundness repair (selector float zero). Grant decode on every contract-major now
  rejects an `equals` value or `one_of` member that contains a float-tagged zero at any depth,
  whatever its sign or spelling (`0.0`, `-0.0`, `-0e0`, an underflowing `1e-400`). Before, the
  reference compared floats with `===`, so `0.0` and `-0.0` were different selector values here
  while the Python, Go, Rust and TypeScript verifiers treated them as equal: the same
  issuer-signed grant verified in one and failed in another. No conforming producer emits such a
  value (RFC 8785 writes a float zero as `0`, which decodes integer-tagged); verifiers accept the
  non-JCS bytes that carry one. Range bounds are unchanged and compare numerically.
  `grant_signing_input` on every major now refuses a float-tagged integral selector value
  (`0.0`, `10.0`), which it used to sign as an integer. New additive corpus
  `bap-selector-float-zero/1` (161 cases, index SHA-256 `97fd7a99cdbf3acc0a78f5ba1c6556e23a63e0ad66f533e31fd69f82dc5f3003`), generated by
  `conformance/generators/build_selector_float_zero.mjs` and run by the verifier CLI and
  `mix conformance.verify`. Every existing corpus index and verdict is unchanged. Governance
  states that conforming verifiers disagreeing on the same signed bytes are a soundness failure.
- The Python, Go and Rust SDKs implement erratum 6 with the same clauses and run the certified
  `bap-selector-float-zero/1` corpus; each failed 92 of its 161 cases before the change. The Go
  and Rust SDKs vendor byte-identical snapshots.
- Independent Node runners: the integer lexeme `-0` decodes to integer 0 and selector identity
  compares tags first, then values numerically (`grant_proof_independent.mjs`);
  `corpus_independent.mjs` tags numbers from their raw lexeme instead of `Number.isInteger`,
  decodes grant payloads as received instead of requiring JCS bytes (matching the reference, with
  claim-type, lexeme-length and exact-magnitude checks kept), and runs the new corpus for
  contract-major 1 with `--selector-float-zero`.
- Replace retired test tooling jsonschex 0.10.0 (Hex security retirement) with the non-retired 0.11 line.
- Release gates. `mix secrets` now runs the same `scripts/secret_scan.sh --check` sensitivity
  self-check CI runs (which ends with the history scan), then the staged and unstaged scans; before,
  it skipped the self-check. The conformance, chain/archive and spec-facts mutation batteries
  collect every entry's outcome and halt with status 3, naming each failed entry, without printing
  their `ok` line when any mutation survives or a baseline is not green. The conformance and
  chain/archive batteries stop with status 2 and a clear message unless `node --version` equals
  the `.tool-versions` pin (22.23.1). `verification.performance` sets a reduction ceiling on
  rejecting over-limit request arguments on every contract-major. New
  `scripts/registry_checksum_readback.exs` compares a published release's Hex registry checksum
  (and optionally a local archive) with the attested `SHA256SUMS`, for the post-publish read-back.
- The three in-repo verifier SDKs (`sdks/python`, `sdks/rust`, `sdks/go`) implement erratum 3's
  producer-canonicality refusal on their assembly and anchored-export-encoding surfaces, with the
  same clauses as the Elixir reference and the graduated TypeScript verifier: Ed25519
  (`signature_gate` — R canonical, R not of small order, S < L) at every Ed25519 assembler
  (contract-majors 1 and 2, local loopback, role attestation, content assertion) and at every
  major's export encoding; ES256 (the canonical-signature rule `0 < r < n`, `0 < s ≤ ⌊n/2⌋`) at
  the v3 assembler and export encoding. Each SDK vendors and runs the certified
  `bap-producer-canonicality/1` corpus (75 cases, index SHA-256 `99ee6fbb…`, red-first before the
  gates, each gate defect-injected red after them). No verification verdict changes anywhere;
  every existing corpus agrees at unchanged counts in all three SDK suites.

### Changed

- Governance states the package version rules from 1.0.0: a new contract-major, sibling profile,
  public function, optional argument, or defaulted struct field is a minor release, and so is a
  soundness repair that meets every governance condition or a raised toolchain floor; an
  editorial erratum is a patch; removing a namespace or changing a struct field, arity, return
  shape, enforced key, or documented error is a new package major. Package versions ("package
  1.x") and contract-majors ("contract-major N") are separate numbers. Decisions publish through
  governance, the specifications, and the errata registry; sibling profiles first ship in a minor
  release instead of a breaking `0.x.0`.
- The specifications drop their YAML front matter (HexDocs printed it as text), link every RFC
  citation, and state in their revision headers which errata each revision carries. Every
  shipped document cites the specification, governance, or errata text instead of an unpublished
  decision-record number. The implementer's guide covers contract-majors 2 and 3 and the selector
  float-zero corpus.
- Development tooling: sbom 0.12.1 (with purl 0.5.2) accepts hex_core 0.21, so the `hex_core`
  override is gone and hex_core 0.21.1 arrives dev/test-only through sbom. CI action pins move to
  setup-node 7.1.0, setup-python 7.1.0, upload-artifact 7.0.2, setup-ocaml 3.9.1 and setup-ruby
  1.338.0. The unpublished Rust SDK takes serde_json 1.0.154 and zeroize 1.8.2. The package has
  no runtime dependencies, so nothing shipped changes.
- Release reproducibility: the Hex client is pinned in `.hex-version` (2.7.0). CI, the
  supply-chain attestation and the release-candidate gate use it, and the gate refuses another
  client, because Hex writes its own version into the archive metadata and a different client
  builds different archive bytes. The supply-chain workflow installs Node 22.23.1 like CI instead
  of using the runner image's Node.

## [0.9.1] — 2026-10-31

### Documentation

- Architecture decision records are local, ignored files and are excluded from the public source,
  Hex archive, and HexDocs. Historical references no longer link to removed records.
- Installation examples, the Livebook, and security guidance point to this ADR-free release.
- No runtime, public API, wire format, or verification verdict changes.

## [0.9.0] — 2026-10-29

A clarifying minor release (erratum 3). No verification verdict and no verdict of any existing
certified corpus changes.

### Fixed

- Compact assembly and anchored-export encoding now refuse a signature that no conforming signer
  emits, and they never rewrite accepted signature bytes. Under contract-major 3 (ES256) the
  refusal covers `r` or `s` equal to zero or at least `n`, and high-S (`s > ⌊n/2⌋`). Under
  contract-majors 1 and 2 and the local-loopback, role-attestation and content-assertion profiles
  (Ed25519), it covers `S ≥ L` and an `R` that is non-canonical or of small order. Every BAP
  verifier already rejected these signatures. Only generic JOSE consumers outside BAP would accept
  a high-S ES256 signature. Signers must normalize to low-S before assembly (`spec/bap-v3.md`
  § 3.2). The adapter and the TypeScript signer already do. Erratum 3. No verdict of any existing
  certified corpus changes, and the new cases are in `bap-producer-canonicality/1`.
- `spec/bap-v1.md` § 14.1 (and its `docs/protocol-v1.md` view) counted "up to eight distinct
  mixed-order encodings" of one key. The eight encodings are the prime-order key and up to seven
  mixed-order variants. Erratum 4, editorial; no requirement, verdict, or corpus changes.
- The contract-major specifications wrote section anchors as kramdown `{#id}` heading attributes,
  which HexDocs and GitHub print literally, so links such as `spec/bap-v1.md#strict-ed25519`
  went nowhere. Each anchor is now an explicit `<a id>` element with the same id, and the
  `spec/bap-v1.md` cross-references are Markdown links. Erratum 5, editorial; no heading wording,
  requirement, verdict, or corpus changes. `mix quality` now checks every local link and anchor
  in the built HexDocs (`mix docs.links`).
- The scratch repositories of the private-identifier guard tests, the secret-scan self-check, and
  the two history-privacy architecture tests turn off git's automatic maintenance
  (`maintenance.auto` false, `gc.auto` 0). From git 2.54, `git commit`, `merge`, `fetch`, and a
  push into a local bare remote start a detached repack that keeps writing into `.git` after the
  command returns; on hosted CI (git 2.55) it can race the scratch cleanup and fail with
  "directory not empty". A new architecture test requires every script or test that runs such a
  git command to set both values. Test and tooling only; no package, wire, or verdict change.
- The development tooling lock moves hex_core 0.20.0 → 0.20.1, clearing EEF-CVE-2026-96596
  (MEDIUM: `erl_tar:extract/2` loops forever on a GNU sparse member with a trailing hole;
  fixed in 0.20.1). hex_core arrives dev/test-only through the `sbom` tool under the existing
  `~> 0.20.0` override, whose rationale is unchanged (sbom 0.11.0 still requires `~> 0.19.0`);
  the shipped package has no hex_core dependency. `mix hex.audit` and `mix deps.audit` are
  both clean.
- Latest-first tooling currency bump: `ex_json_pointer` 0.8.0 → 0.9.0 in the dev/test lock
  (transitive through the documentation tooling; not shipped). The currency gate found the
  drift before the release battery did.

### Added

- `BoundedAuthorityProtocol.Es256`, the single public owner of the ES256 rules, mirroring
  `BoundedAuthorityProtocol.Ed25519`: `canonical_signature?/1` (the shared `0 < r < n`,
  `0 < s ≤ ⌊n/2⌋` predicate that verification and the producer surfaces both call),
  `acceptable_public_key?/1` (uncompressed SEC1, coordinates below `p`, on the curve),
  `verify_strict/3`, and the producer utilities `normalize_low_s/1` (pure `s → n − s` when high,
  refusing out-of-range scalars, idempotent; never applied by assembly) and `der_to_raw/1`
  (strict DER `ECDSA-Sig-Value` to raw `r || s`, refusing non-minimal or negative INTEGERs,
  long-form or mismatched lengths, trailing bytes, and out-of-range scalars; the output is not
  normalized). The internal v3 suite module and the EC JWK codec delegate to it.
- The additive producer-canonicality conformance corpus `bap-producer-canonicality/1`, revision 1,
  at `priv/conformance/producer-canonicality` (75 cases in 4 families; 60 refused, 15 accepted at
  the rule's bound; index SHA-256
  `99ee6fbbf32758b03d4630647f721d8d4e349f0628d860493d667fe6e762c601`), generated keylessly by
  `conformance/generators/build_producer_canonicality.mjs`. The verifier CLI and
  `mix conformance.verify` run it.
- Requirement ids `REQ1-SIGNING-ed25519-producer-canonical`,
  `REQ2-SIGNING-ed25519-producer-canonical`, and `REQ3-SIGNING-producer-canonical`; the
  local-loopback, role-attestation, and content-assertion assembly requirements carry the same
  clause.
- Livebook walkthrough sections for strict Ed25519 verification (rule S1), with forged keys and
  signatures from the certified strict-Ed25519 corpus, and for contract-major 3 (ES256) with
  ephemeral P-256 keys. The walkthrough test now evaluates every notebook cell in order.

### Changed

- The keyless part of Ed25519 rule S1 (`R` canonical and not of small order, `S < L`) is factored
  out of the verification gate, so the gate and every Ed25519 producer surface share one owner.
- The `REQ3-SIGNING-range` red-proof disclosure (the ES256 activation decision record) is withdrawn: the producer surfaces
  isolate each range clause, and the matching mutation-battery entries run red.

### Documentation

- README quickstart and a rewritten getting-started guide (issue, prove, and verify a request,
  handle a rejection, rotate issuer keys, test an integration). Their Elixir blocks run in the
  test suite (`test/docs_samples_test.exs`).
- HexDocs is organized as Start here, Guides, Reference, Specifications, and Decision records,
  with the modules grouped by contract-major, signature suite, sibling profile, and conformance.
  The `BoundedAuthorityProtocol.V1` functions document their arguments, results, and errors;
  `Bounds.coerce/1` is documented; `Ed25519` and `Es256` carry their first release.
- Unreleased additions are marked with one phrase, `(unreleased, planned for 0.9.0)`, and a
  docs-currency test requires it while the source is ahead of the release and forbids it after:
  the release edit is deleting that phrase.
- `SECURITY.md` points reporters to GitHub private vulnerability reporting (the Security tab,
  "Report a vulnerability").
- Usage rule 18: hand assembly only a canonical signature (convert DER and normalize to low S
  first for ES256).
- The consumer conformance path is one that works from a project: `mix run -e
  'BoundedAuthorityProtocol.Conformance.Cli.Main.main(["--corpus", ...])'` or
  `mix escript.install hex bounded_authority_protocol 0.8.0` (README, getting started, usage
  rule 14, upgrading). The docs-currency test pins the escript version to the latest release.
- Every assembler and anchored-export encoder documents its 0.9.0 refusal of a non-canonical
  signature; the upgrading guide names every affected function.
- The docs-samples test also checks each sentence that states a block's value, and the
  release-time marker is required on every surface that describes an unreleased addition,
  including the API docs in `lib/`.
- HexDocs titles: README shows as "Overview", the generated contract-major 1 view and the protocol-boundary decision record have readable
  titles. The Elixir and OTP requirement says what Mix enforces and what CI tests.
- The Python, Rust, and Go SDK READMEs and deployment notes are rewritten: they state that the
  SDKs are unpublished and give a working install from a checkout, list the full API of each
  (contract-majors 1, 2, and 3 and the sibling profiles), and state the open gaps (erratum 3 is
  not implemented in any SDK; facts rendering is not redacted). Each SDK's quickstart runs in
  its own test suite (Go `Example`, Rust doctest, Python pytest). The TypeScript deployment
  notes describe the published 0.7.0 package.

## [0.8.0] — 2026-10-13

The strict Ed25519 verification release: a soundness repair (erratum 2,
the strict-Ed25519 erratum decision record). Upgrade from every earlier
release; see the security advisory for affected versions and details.

### Security

- Ed25519 verification accepted signatures under small-order and non-canonical public-key and
  point encodings, which can verify without any private key. Every Ed25519 surface now applies
  strict verification before the backend: point encodings must be canonical, small-order keys
  and `R` values are rejected, the scalar must be below the group order, and the cofactorless
  equation is mandatory. This covers grants, holder proofs, boundary anchors, key transitions,
  anchored exports, local-loopback proofs, role attestations, and content assertions under
  contract-majors 1 and 2. Contract-major 3 (ES256) is unaffected.

### Added

- `BoundedAuthorityProtocol.Ed25519`, the single public owner of the rule:
  `verify_strict/3`, `acceptable_public_key?/1`, and `torsion_free?/1`. Hosts must call
  `torsion_free?/1` at key enrollment, at trust install, and before any deny-list or revocation
  comparison (`REQ1-HOST-ed25519-torsion-free`).
- The additive strict-Ed25519 conformance corpus `bap-strict-ed25519/1`, revision 1, at
  `priv/conformance/strict-ed25519` (2,410 cases in 14 families; index SHA-256
  `732117dfe1881b553998126a9524a95bfb1171257e388d5430a0a099f2287cb9`). Its verdicts come from an
  independent arithmetic generator. It includes the 12 published "Taming the many EdDSAs" test
  vectors (Apache-2.0, attributed in `NOTICE`), named small-order-key vectors for both contract
  majors, and a JCS UTF-8 boundary family. The verifier CLI
  and `mix conformance.verify` run it beside the existing corpora.
- Requirement ids `REQ1-SIGNING-ed25519-canonical-point`, `-small-order-reject`,
  `-scalar-range`, and `-cofactorless`; `REQ1-KEY-ed25519-acceptable`;
  `REQ1-HOST-ed25519-torsion-free`; and the five matching `REQ2-*` ids, mapped in the
  requirement map.
- A soundness-repair change class in the governance policy, the charter, the errata registry, and
  `SECURITY.md`.

### Changed

- Producers and key functions refuse a public key that fails key acceptance (wrong width,
  non-canonical, small order, or off the curve) with `{:error, :invalid}`: proof, boundary-anchor,
  key-transition, and role-attestation signing-input construction, `V1.Jwk.encode_public/2`,
  `V1.Jwk.decode_public/2`, and the JWK thumbprint functions. Honest RFC 8032 keys are unaffected.
- `spec/bap-v1.md` and `spec/bap-v2.md` move to revision 2, and the local-loopback,
  role-attestation, and content-assertion specifications state the rule. The security-release and requirement-identifier decision records
  carry amended-by notes.
- Every existing certified corpus (v1, v2, v3, local loopback, role attestation, and content
  assertion), its index digest, and its verdicts are unchanged.
- The three independent Node conformance runners apply the strict rule at their Ed25519
  verification and key-acceptance points, and `conformance/corpus_independent.mjs --strict` runs
  the strict-Ed25519 corpus. CI pins the runners' Node release exactly (22.23.1).

### Fixed

- Upgrade the development SBOM tool to sbom 0.11 and protobuf 0.17.1, which fixes
  EEF-CVE-2026-104635 in protobuf JSON decoding. Generate release and tooling SBOMs
  through `mix run --no-start` with explicit SBOM startup, keeping hex_core's
  SSH-dependent application stopped. If the OS certificate store is unavailable,
  reuse Hex's CA certificates for license metadata requests. The production
  dependency boundary is unchanged.

## [0.7.0] — 2026-09-28

The content-assertion release: the second sibling attestation profile. No contract-major,
loopback, or role-attestation byte, verdict, or certified pin changes.

### Added

- A standalone `bap-content-assertion/1` profile with explicit expected content and
  context binding, bounded byte-domain content hashing, and pairwise successor checks
  (`BoundedAuthorityProtocol.ContentAssertion.V1`). The profile carries no content
  semantics or authorization decision. The content-assertion profile decision record records the design and its release
  requirements; `spec/bap-content-assertion-v1.md` is the normative contract.
- The certified content-assertion corpus, revision 1 (131 assertion, 9 content-digest, and
  14 successor cases; index SHA-256
  `14b7436ccf7cc91fece52a1578c3760df6720a93494d147ee5ab523e2ce21876`), the
  `mix content_assertion.verify` check, and its red-capable mutation gate. The Python, Rust,
  and Go SDKs and the graduated TypeScript verifier implement the profile against it.
- A tightenable `content_bytes` ceiling of 65,536 bytes. Existing wire profiles and
  certified corpora retain their meanings.
- A runnable content-assertion section in the Livebook walkthrough.

### Changed

- `%BoundedAuthorityProtocol.V1.Bounds{}` enforces the new `content_bytes` key. Code that
  builds the struct literally must add it; `Bounds.new/1`, `Bounds.maximum/0`, and override
  maps are unaffected.
- `V1.SigningInput` admits the `:content_assertion` kind, whose exact protected header is
  accepted only through the new namespace.
- CI runs on Linux only (the Linux-only CI decision record); developer portability across macOS, Linux, and Windows
  is unchanged. The supply-chain workflow runs on manifest changes, weekly, and on demand.

## [0.6.2] — 2026-09-24

The cross-Elixir compilation and scan-sensitivity repair patch over 0.6.1: no verdict,
wire-format, or public-API change — the certified corpora, index digests, decision records, and
governance documents are unchanged.

### Fixed — cross-toolchain compilation and gates

- The role-attestation codec's closed-role membership and bounded-size checks compile to
  version-stable Erlang on every supported Elixir. On Elixir 1.18/1.19, expression-position `in`
  over a literal list compiled to a `:lists.member/2` import and runtime-bounded ranges compiled
  to `Enum.member?/2` + `Function.identity/1` imports — findings the compiled purity and package
  gates correctly rejected, turning the 1.18/1.19 CI lanes red (RED observed locally on
  1.18.4-otp-28: three failures; GREEN after the rewrite on 1.18.4-otp-28 and a fresh
  1.19.5-otp-28 build). The membership and bounds are explicit comparisons, verdict-identical on
  every input for every bound the public API can reach — `Bounds` coercion admits positive
  integers only, and for n >= 1 the predicates agree on every input (the unreachable n <= 0
  forms differed only in dead code); the gates are unchanged and no allowances were added.
- The secret-scan sensitivity battery plants a JWT fixture at the role-attestation corpus path:
  the BAP-23 allowlist path group for
  `priv/conformance/attestation-profiles/role-attestation/v1/` had no sensitivity fixture, so
  the scan gate's allowlist path-sensitivity self-check failed ("missing 1 path group");
  observed RED then GREEN locally under the pinned Gitleaks 8.30.1.
- The role closed-set conformance mutation follows the codec's rewritten membership anchor.

### Documentation

- Standards-review docs sweep: SECURITY.md, the getting-started guide, and the Livebook
  walkthrough cite the then-current 0.6.1 registry release (checksum read back from the Hex
  release API on 2026-09-24; this release's own version surfaces carry 0.6.2); AGENTS.md's
  accepted decision-record range is corrected and its current-state
  paragraph records the 0.6.0 role-attestation release and the public holder-side signer
  companion; the SDK README and the four SDK deployment guides stop describing the graduated
  TypeScript SDK as in-tree (npm scope corrected to `@bounded-authority-protocol/verifier`) and
  state the certified corpora each SDK actually runs (v1 283 / v2 268 / v3 292 plus the
  loopback and role-attestation profile corpora), with the Rust supply-chain table gaining the
  `p256`/`zeroize` ES256 backend rows; the interoperability report's TypeScript row and
  publication-status claims are corrected to the graduated repository; the guides index carries
  the role-attestation spec as a normative authority; and the docs-currency test now pins the
  graduated SDK posture — three in-repo SDKs plus the graduated npm scope and repository,
  rejecting the retired `@bounded-authority/verifier` scope and any in-tree `typescript/` link.

## [0.6.1] — 2026-09-23

The cross-vendor repair patch over 0.6.0: the second independent review family's pass
over the role-attestation landing range (e5cd033..d470814, executed 2026-09-23 after the
account-quota window reset) confirmed eight product findings — three blocking — all fixed
in `05c24a8` with RED→GREEN evidence and the full quality gate green (584 tests, 100%
coverage). The certified corpus bytes, index digests, and the role-attestation profile decision record are unchanged; verdicts
on legal input do not move. The full adjudication with severities and repair evidence is
retained in the repository's local review records.

Why each fix, in one line each: (1) the Python codec joined member sets with a comma —
an exact-set comparison closes a `KeyError` collision; (2) trailing-newline identifiers
and URIs were accepted by Python and rejected by the reference — full-string checks at
all three positions; (3) the standalone attestation ignored the caller-tightened
`anchor_bytes` ceiling on all four surfaces (upgrading the review's original
source-inference to OBSERVED — the earlier "verdict-neutral" characterization is
REFUTED by a caller-tightened runtime counterexample); (4) producers now validate their
emitted JSON under decoder limits and project final compact/segment sizes; (5) the
in-repo SDKs' StringOrUri handling diverged from the reference on IP literals, ports,
and escapes — profile-local helpers align them; (6) Python caller-input validation
returned raw exceptions instead of the closed error; (7) Elixir raised `KeyError` on
incomplete caller structs instead of `{:error, :invalid}`; (8) the Rust parser decoded
segments before checking limits — encoded-size and projected-decoded-size checks now
precede base64 decoding (Go and Python preflight all three segments equivalently).

### Fixed — role-attestation validation

- Role-attestation signing-input production applies the same caller-tightened parser
  limits as assembly, decoding, and verification. The Python, Rust, and Go SDKs also
  enforce the standalone attestation's `anchor_bytes` ceiling.
- Python rejects ambiguous member names, newline-bearing identifiers, invalid URI
  literals, malformed Unicode, and unknown bounds through the closed error result.
  Go and Rust align role-attestation URI validation with the profile's StringOrUri rules.
- Elixir returns `{:error, :invalid}` for incomplete caller-supplied attestation,
  signing-input, expected-context, and attestor-key structs. The Python, Rust, and Go
  SDKs check segment limits before base64 decoding.

## [0.6.0] — 2026-09-23

The role-attestation release (the role-attestation profile decision record: the first `0.x.0` bearing the
`bap-role-attestation/1` profile). Additive public surface — the
`BoundedAuthorityProtocol.RoleAttestation.V1` namespace and the certified profile corpus —
with zero verdict changes on existing input; also carries the v1/v2 closed-error-shape fix
found by the profile corpus's ES256 confusion case.

### Added — the role-attestation sibling profile `bap-role-attestation/1` (the role-attestation profile decision record; the BARA RA11 prerequisite)

- `BoundedAuthorityProtocol.RoleAttestation.V1` — the protocol's first sibling attestation
  profile: a standalone, grant-unbound compact JWS (protected `typ: "ba+role-attestation"`)
  binding a subject key to `issuer` or `holder` for a bounded window. Four surfaces:
  `attestation_signing_input/2`, `assemble_compact/{2,3}`, `decode_attestation/2`, and
  `verify_attestation/2` (the citation symbol the companion signer's RA11 row names). Facts are
  the anchor posture (`trust: :not_evaluated`, no authorization marker, RFC 7638 fingerprints,
  Inspect-redacted); verification enforces attestor-kid binding, subject binding, structural
  self-attestation rejection, attestation-window containment in the attestor key window, and the
  half-open `[nbf, exp)` now-window. Normative profile `spec/bap-role-attestation-v1.md` with
  `REQ-RA1-*` ids and a per-profile requirement map; profile corpus (40 cases, revision 1,
  certified index pinned in the ExUnit suite, the map, the spec, and every SDK) at
  `priv/conformance/attestation-profiles/role-attestation/v1/`, verified end-to-end by
  `mix role_attestation.verify`; ten red-capable mutation-battery entries; registries gain the
  `ba+role-attestation` typ and the attestation-profiles family; governance, the standards-track
  evolution contract, and the requirement-identifier decision record carry the labeled role-attestation class amendments (the budget-window decision record's
  budget-window posture unchanged). The in-repo Python/Rust/Go SDKs implement the four surfaces
  with the in-repo corpus and its digest asserted at test load.
- Release preconditions for any `0.x.0` bearing this profile (release itself remains the owner's
  decision): the graduated TypeScript verifier surface coordinated at corpus freeze, the
  authority-runtime issuance receipt, and the companion-signer consumption receipt in their own
  repositories.

### Fixed — v1/v2 closed error shape under wrong-width signatures

- `V1/V2.decode_grant/2`, `decode_proof/2`, and the verify paths behind them returned a bare
  `false` (a `with` guard leak) instead of `{:error, :invalid}` for compacts whose signature
  segment decodes to a byte length other than the suite width — found by the profile corpus's
  ES256 confusion case (a raw ECDSA `r||s` signature), fixed V3-style (parse normalization),
  pinned RED-first by `test/bounded_authority_protocol/signature_width_error_shape_test.exs`. No
  accept/reject verdict changes; the v1/v2/v3 corpora and their certified pins are unchanged.

### Fixed — cross-vendor review legs (2026-09-22, over the BAP-23 range)

- The Python and Go SDKs now magnitude-bound the attestor context's window endpoints in
  `verify_attestation` (parity with the Elixir reference's `ContextValidation.historical_key`
  and the Rust gate): a caller context with `valid_from` below or `valid_before` above the
  integer magnitude ceiling returned `ok` where the reference returns `{:error, :invalid}` —
  containment alone is trivially satisfied by an out-of-magnitude window, so nothing
  downstream rejected it. Pinned RED-first in both SDKs' attestation suites; corpus verdicts
  and the certified index are unchanged (all corpus contexts are in-magnitude).
- `scripts/check_role_attestation.exs` (`mix role_attestation.verify`) now compares the
  computed corpus index digest against the certified pin
  (`REQ-RA1-CONFORMANCE-certified-pin`) instead of only echoing it — a regenerated,
  self-consistent corpus previously verified green. Red-proven by a bogus-pin probe.
- The Python SDK's decode surface carries the raw 32-byte subject `public_key` (the member
  shape the Elixir/Go/Rust decode surfaces all expose) instead of a derived thumbprint — an
  unpublished-surface alignment so the four bindings decode portably.

## [0.5.1] - 2026-09-22

### Docs maintenance — the published doc set trimmed to implementer-facing content

- The Hex package and hexdocs now carry the implementer-facing set only: the four specs, the
  decision records, the guides/deployment/livebook docs, the normative contracts
  (registries, requirement map, standards track, governance, threat model, errata,
  interoperability report, protocol charter, conformance contract), and the package
  essentials. Removed from the published set while remaining tracked in the repository: the
  IANA pre-filing registration drafts (`docs/design/iana/`), the AP2 and A2A interop design
  profiles, the successor-major activation checklist, and the superseded 0.1.0
  release-candidate contract. The BAP-17 offline requirements input moved out of the tracked
  tree into local untracked storage (the offline floor-limit decision record remains the mechanism of record). No code,
  wire, bound, or verdict change; the package census and the published-surface hygiene
  roots are re-pinned to the trimmed set.

## [0.5.0] - 2026-09-22

### Added — spec-facts extraction baselines for contract-majors 2 and 3 (the activation records' deferral closed; no wire or API change)

- The spec-facts drift gate is major-keyed: `spec/bap-v2.md` and `spec/bap-v3.md` now carry
  closed facts-anchor sets (suite identity, domain separators, selector kinds; v3 adds the
  protected-header members, the §3 suite rules — curve, EC JWK member set, 32/65/64-byte
  fixed widths, raw `r || s`, the low-S rule, the rejected-encoding list — and the §5
  fixed-width constants), extracted deterministically and frozen byte-for-byte in
  `spec/facts/baseline-v2.json` / `baseline-v3.json`. Rule 1b reds on any silent
  successor-major normative edit exactly as for v1; the v1 baseline bytes, ten-anchor set,
  and extraction output are unchanged. The spec-facts mutation battery grows from seven to
  sixteen legs, each observed RED on the real tree before wiring — including the literal-NUL
  separator class of the requirement-map incident (a NUL byte where the two-character `\0`
  text belongs is not matched and reds).

### Added — ES256 contract-major activation: the `BAP3-ES256-SHA256` suite (the ES256 activation decision record)

- `BoundedAuthorityProtocol.V3` — the complete contract-major 3 closed profile: ECDSA over
  NIST P-256 with SHA-256 (`alg: "ES256"`), RFC 7518 §3.4 raw `r || s` signatures with the
  REQUIRED low-S canonicality rule and `0 < r < n` range checks, EC JWK holder keys
  (`{crv,kty,x,y}`) with RFC 7638 thumbprints, 65-byte uncompressed-SEC1 raw keys,
  `BAP3-*` domain separators, payload `v: 3`, and the five-kind selector algebra inherited
  from v2. Each major verifies under its own complete closed profile; v1 and v2 reject v3
  bytes and v3 rejects v1 and v2 bytes (the ES256 activation decision record).
- `spec/bap-v3.md` — the normative v3 profile; the certified v3 corpus
  (`priv/conformance/v3/corpus`, 292 cases across 28 surfaces, revision 1) verifies 292/292
  through the CLI with its index SHA-256 pinned in the major-keyed certified map. The
  signature-canonicality matrix (high-S, tampered r/s, zero r/s, r/s ≥ n), the EC JWK
  closed set, the thumbprint member set, and the cross-major rejections (v1 and v2 bytes on
  every artifact family) are corpus-certified. Six verdict-provable mutation-battery
  entries executed RED; two battery adjudications are disclosed in the requirement map § v3
  (the r < n check is backend-coincident for out-of-range scalars — unit-level proof; v3
  cross-major rejection is pinned at both the alg and the v claim).
- Registries: `BAP3-ES256-SHA256` active; the `lte`/`gte` rows name the v3 closed profile
  alongside v2's; the ML-DSA anticipated index advances to the next successor major. The
  post-quantum suite decision record carries a dated reconciliation note for its stale `BAP2-*` family index.
- The in-repo SDKs (Python, Rust, Go) gain v3 namespaces, runners, and permissiveness
  mutation gates; the TypeScript cohort (graduated verifier repository snapshot-bump,
  signer suite adoption) and the A2A `BA-Grant`/`BA-Proof` drill land with this
  activation per the ES256 activation decision record. The §8 kiosk-demo refresh named in the 2026-09-20 cohort
  decision is withdrawn — its demo tree no longer exists (owner decision, 2026-09-22;
  see the ES256 activation decision record's post-acceptance note).

### Added — UCP riding-point decision (the UCP riding-point decision record) and A2A venue extension draft (no wire or API change)

- The UCP riding-point decision record (resolution of the open question
  both interop profiles recorded; UCP read first-hand at pinned `d3ccb55`): the riding point is
  transport-composed — UCP's A2A transport is covered as-is by the A2A binding profile for
  deployments speaking A2A 1.0 (UCP's published A2A binding is at 0.3-era shapes —
  `X-A2A-Extensions`, `message/send` — and reconciliation is UCP's); UCP's MCP transport is the
  BAP-08 extension's scope; a UCP-REST binding profile is the named follow-on under the
  project's own reverse-domain prefix (UCP reserves `dev.ucp.*`); Embedded is out of scope. The
  three-layer composition doctrine (UCP's RFC 9421 transport identity, BAP invocation authority,
  AP2 payment authorization — disjoint surfaces, none covers another) extends the AP2 profile's
  §6 separation. Closed-by pointers added to both design profiles and the ROADMAP.
- `docs/extensions/a2a-capability-binding.mdx`: pre-submission venue-facing restatement of the
  A2A binding profile for the A2A ecosystem (not proposed to A2A's sponsorship/TSC path; no
  submission implied). Carries the scope boundary, the one-hop-only rule, the
  `required: true` narrowing consequence, name-registration status (incl. `cnf` correctly
  attributed to RFC 7800, also fixed in the MCP precedent), and the no-implementation status.
  README now names both extension drafts; `AGENTS.md` decision-record roster corrected.

### Added — A2A capability-binding profile (design contract, no wire or API change)

- `docs/design/a2a-capability-binding-profile.md` (owner-approved 2026-09-20 after the design
  method: options analysis over the binding-layer fork, then independent adversarial review of
  both the analysis and the specification — two rounds each, all findings repaired and
  re-verified): the composition contract with A2A at pinned `afda8316` — extension-mechanism
  negotiation (empty `params`, matching the MCP extension precedent) with transport-layer
  proof binding (unchanged `dpop+jwt` bytes; `BA-Grant`/`BA-Proof` HTTP headers and gRPC
  metadata; `htm`/`htu` per binding incl. the commit-pinned gRPC HTTP/2 grammar); the bounded
  per-operation `cast_arguments` projection (per-part content digests inside the v1 §17
  ceilings, tenant coverage including declared absence, one timestamp rendering); `ba_op`
  spelled under a reverse-DNS vocabulary; `aud`/`ba_inv`/nonce placement; graceful degradation
  with the client-optional-narrowing consequence stated; scope excludes custom bindings,
  non-loopback plain HTTP, and push-notification webhooks. Delegation-shaped requirements
  route to the successor-major program as a recorded dependency; UCP riding remains a
  separately-owned open question. Structural-correspondence claims only; no compatibility or
  runtime guarantee claimed.

### Added — AP2 interop profile (design contract, no wire or API change)

- `docs/design/ap2-interop-profile.md` (owner-approved 2026-09-20 after independent adversarial
  review: original FAIL with 3 P2 + 4 P3 findings, all repaired, repair-diff PASS): the
  composition contract with AP2 v0.2 at pinned `e1ea56d` — the ES256 suite bridge contract
  (`BAP<n>-ES256-SHA256`, suite decision pending), the three-spelling digest map (AP2 bare
  base64url / BAP wire-base64url and internal-raw / CAP tagged; lossless byte conversions, no
  value-coincidence claims), the cast-argument projection over integer minor units (the
  integer-vs-float decision is evidence-forced: AP2 schema and SDK integer, v2 same-tag range
  rules; float money content never projects), host-riding questions recorded open (A2A binding
  undecided), the CAP receipt join noted only, and the cross-vectors posture (AP2 ships no
  in-tree vectors — probed with a working control). Structural-correspondence claims only; no
  compatibility or runtime guarantee claimed.

## [0.4.2] — 2026-09-20

### Changed — CI is Linux-only (developer portability, not tri-OS proof)

- Per the owner's directive (the Linux-only CI decision record, superseding the per-push Windows-lane element of
  the toolchain lockstep decision record): CI runs Linux only — the Elixir/OTP compatibility lanes and the
  complete-quality lane. The portability contract for contributors (clone, `deps.get`,
  compile, test on Windows/macOS/Linux) stays enforced in-repo at zero runner cost —
  portable test support, the LF-forcing `.gitattributes`, no POSIX shell inside declared
  gates — and non-Linux lanes run only ad hoc when a platform-specific defect is suspected.
- The architecture gate's jsonschex pin moved to `~> 0.10.0` in lockstep with the batched
  dependency sweep (the requirement bump without the pin mismatch is what reddened the first
  post-sweep CI run).

### Changed — documentation truth: AP2 mapping note, selector-scope paragraph, budget-window record

- The `tracked-authoring-paths` gate admits one previously committed file (from commit
  `47297b8`) at exactly its committed blob, mode, and stage 0. Any other content at that path
  and every other local authoring path in the index or HEAD history remain findings; the
  red-capability self-test covers tampered bytes, a sibling path, wrong modes, and a gitlink.
  The pinned path and blob live in the gate test itself.
- `docs/extensions/ap2-mandate-mapping.md` corrected against first-hand reads of AP2 v0.2
  (pinned `e1ea56d`) and the published specs (OBSERVED: every cited line opened in both trees).
  The grant's analog is now the **open** mandate (issuer-signed, constraint-bearing,
  `cnf`-bound), with the closed-mandate key-binding hop mapped to the holder proof; the stale
  claims that the selector algebra "does not express" range bounds and that range kinds are
  successor-major scope are corrected (contract-major 2 ships `lte`/`gte`; the range-selector and contract-major 2 activation decision records); and
  the note now names the structural differences it previously omitted — mandate chains, the
  verifier-signed Mandate Receipt versus facts marked `not_evaluated`, the fail-closed
  unknown-constraint rule, the Trusted Surface, the signature-suite seam, and selective
  disclosure. Still a pre-submission draft (BAP-08); still no compatibility claim.
- `docs/extensions/capability-authorization.mdx` § 5: the selector-expressiveness paragraph is
  scoped to contract-major 1 (this draft binds `v` exactly `1`) and points at the contract-major
  2 range kinds; the budget-routing sentence now cites the budget-window decision record.
- The budget-window decision record is added: the cumulative-budget posture is
  consolidated as citable law — no cumulative selector kind in any major; enforcement routes to
  issuer attestation or runtime accounting — and the `ba+budget-window` attestation-shape design
  is explicitly deferred (no wire name reserved, no shape specified, no code change). The
  package files list and the published-surface hygiene roots gain the record file, and the
  planned-but-unwritten forward references in the range-selector decision record and the contract-major 2 activation decision record are reconciled to the
  written record.

## [0.4.1] — 2026-09-17

### Changed — self-enforcing toolchain, dependency currency, tri-platform build bar

- The repository now enforces its own toolchain before anything compiles: `config/config.exs`
  raises at config load on an Erlang/OTP major outside the supported set **{27, 28, 29}**
  (floor-limited by the stdlib `:json` module, OTP 27+; probed via asdf and the official Elixir
  docker tags). The assert is not shipped in the Hex package (`files:` excludes `config/`), so
  consumers enforce their own. OBSERVED: Elixir 1.19.6/OTP 26 and 1.18.4/OTP 25 refuse with the
  assert message in throwaway docker copies; Elixir 1.17.3/OTP 27 refuses with Mix's declared-range
  error; 1.18.4/OTP 27 compiles green under `MIX_ENV=test` (the toolchain lockstep decision record).
- Lockstep rule recorded in mix.exs: the declared Elixir range (`~> 1.18`, unchanged), the config
  supported-OTP set, `.tool-versions`, and the CI compatibility lanes move together in one commit.
- `.tool-versions` now declares the test battery's Node dependency (`nodejs 22.23.1`, matching the
  CI quality lane's Node 22): the independent Node verifier runner in `mix test` previously
  depended on whatever `node` the host PATH provided — the dev shell's Homebrew node — and an
  asdf-managed host had no `nodejs` pin at all (OBSERVED: asdf shim exit 126, "No version is set
  for command node", 36 battery failures before the pin).
- The clone → `deps.get` → compile → test contract is now gated for macOS, Linux, and Windows:
  `.gitattributes` forces LF checkout (Git-for-Windows' documented `core.autocrlf=true` default
  would CRLF-convert every tracked text file on checkout and byte-mangle the digest-pinned
  certified corpora — INFERRED from git's default, removed by policy) with the byte-exact
  conformance trees `-text`; the test path lost its POSIX-only mechanisms (portable
  `mix`/`elixir`/`escript` spawning via `test_support/portable.ex`, `System.tmp_dir!()` scratch
  paths, guarded carve-outs for the chmod-based, symlink-based, and POSIX-shell tests); a
  `windows-2025` CI lane runs checkout, `deps.get`, `compile --warnings-as-errors`, and
  `mix test` on every push (OBSERVED green: all 445 tests on the lane's first completed
  run, 2026-09-17). The full `mix quality` battery
  remains POSIX-only tooling (CI or WSL on Windows).
- Dependency currency is gated latest-first by `scripts/check_deps_currency.exs` (Elixir, not
  shell, per the tri-platform bar) inside `mix quality` via `mix deps.currency`: resolvable drift
  fails the gate naming the packages; resolver-rejected updates are reported with requirement
  chains; no rendered table is an unverified state and fails. OBSERVED: green on the current lock;
  a fabricated dialyxir revert in a scratch copy exits 1 naming dialyxir; the no-table path exits 1
  (the dependency-currency decision record).
- Lock updated to the resolvable latest: dialyxir 1.4.8, ex_doc 0.40.4, ex_json_pointer 0.8.0.
  `jsonschex` stays `~> 0.9.2` with an inline reason (0.10 is a major jump pending review against
  the certified corpus schema gates).

### Documentation — published-surface corrections

- v1 privacy considerations gain "Digests of guessable inputs": facts carry unkeyed digests, so
  a request digest over a small argument space can be confirmed by hashing candidates, and
  deployments sharing facts or evidence outside their trust boundary treat it as revealing the
  request (informative; applies to v2 by incorporation).
- v1 residual risks now state that the evaluation time and the cast arguments are part of the
  expected context, so taking either from the presenter reopens time and request substitution.
- v2 selector section gains informative issuer guidance: pair each range selector with an
  `equals` selector on the unit member it depends on, and shape arguments so bounded values are
  object members, because paths never index arrays.
- The range-selector decision record, the contract-major 2 activation decision record, and the roadmap now mark the budget-window decision record (`ba+budget-window`) as planned and not
  yet written; the contract-major 2 activation decision record no longer calls the name reserved, because the registry does not list it.
- The contract-major 2 activation decision record gains a release note: 0.4.0 published contract-major 2 alongside v1 without
  deprecating v1. The roadmap records the 0.4.0 v2 release and the npm TypeScript verifier.
- AGENTS.md lists the accepted decision records.
- Two typos in the v1 spec are fixed.

### Changed — repository hygiene

- Correction: the September 14 rewrite removed the legacy local-harness tree from `main`
  and release-tag histories, not from every public ref. OBSERVED September 15 UTC with a fresh
  mirror fetched using `+refs/*:refs/*`: ten other branch histories retain the legacy tree,
  and retained pull-request refs expose earlier confidential-content snapshots. Provider
  removal remains open under the public-history boundary decision record. Historical commit SHAs cited in earlier entries
  may describe pre-rewrite history.
- Remaining internal-tooling phrases in the verifier-SDK and SDK-graduation decision records are reworded, and the published
  surface check now rejects those phrases. OBSERVED: the expanded check rejects the original
  the verifier-SDK decision record content and passes the corrected surface. The already-published 0.4.0 artifact
  retains its original documentation; this correction does not replace that release.
- The package hygiene check includes hidden files and asserts that its roots match the package
  declaration. OBSERVED: the earlier implementation failed both the planted hidden-file check
  and the root-equality check; the corrected focused run passed all 11 hygiene and structural
  checks (`mix test test/architecture/tracked_authoring_paths_test.exs
  test/architecture/published_surface_hygiene_test.exs`). The structural check rejects tracked
  local authoring-tool directories and their HEAD history, with one byte-pinned public manifest
  exception. These controls do not establish confidential-term coverage or provider removal.

## [0.4.0] — 2026-09-14

### Changed — TypeScript SDK graduated (the SDK-graduation decision record executed)

- The TypeScript verifier SDK moved out of this monorepo on its first npm publication and now
  lives at
  [baselabs/bounded_authority_protocol_typescript](https://github.com/baselabs/bounded_authority_protocol_typescript),
  published as
  [`@bounded-authority-protocol/verifier@0.2.0`](https://www.npmjs.com/package/@bounded-authority-protocol/verifier)
  (wire contract-majors 1 and 2, vendored certified corpora, self-contained CI, two-stage npm
  publishing: the workflow stages with provenance and a human approves under 2FA). `sdks/`
  now authors the Python, Rust, and Go SDKs; the `sdk-publish-guard` continues to reject
  registry-publish infrastructure for them in this repository. A corpus rotation is now a
  snapshot-bump commit in the graduated repository; the corpus-sync gate returns to the four
  Rust/Go snapshots. The 0.1.0 seed under the retired `@bounded-authority/verifier` scope was
  unpublished during the rename window.

### Changed — published-surface hygiene

- The shipped package surface (changelog and four decision records) carried internal
  authoring-tooling vocabulary from the hardening-arc records; identified references were
  reworded to neutral engineering vocabulary. The unreleased correction above records two
  phrases that remained in the 0.4.0 documentation.
  A new red-capable published-surface hygiene gate scans the package-files boundary for the
  vocabulary class (word-bounded; the deliberately unshipped ROADMAP excluded; legacy
  local-tooling paths banned while ordinary cryptographic vocabulary stays legal).

### Added — v2 contract-major: `lte`/`gte` range selector kinds activated (BAP-21, the contract-major 2 activation decision record)

- Add the successor contract-major 2 profile per the successor-major charter activation
  checklist and the range-selector decision record: payload `v: 2`, the
  `BAP2-Ed25519-SHA256` suite under the major-bound naming scheme, `BAP2-REQUEST\0` /
  `BAP2-CHAIN\0` / `BAP2-ARCHIVE\0EXPORT\0` domain separators, and the two inclusive
  one-sided selector kinds `lte`/`gte` (same-tag numeric operands, inclusive comparison on
  the closed numeric domain, intervals as conjunctive composition, cross-tag and
  non-numeric operands fail closed; strict kinds excluded permanently).
- Add the `BoundedAuthorityProtocol.V2` Elixir namespace (explicit façade mirroring the v1
  surface; version-neutral algebra single-sourced from the v1 modules) and the normative
  spec [`spec/bap-v2.md`](spec/bap-v2.md) (complete closed profile; v1 sections
  incorporated by enumerated reference).
- Add the certified v2 conformance corpus (`priv/conformance/v2/corpus`, 268 cases across
  the 28 surfaces and 16 classes, revision 1; certified index SHA-256
  `6de6289b7f47b0e0a78ea4610e7844a0f1d5247d8eace02ec9cf9841308f13d0`). The verifier CLI
  verifies both corpora with major-keyed certification; `mix conformance.verify` runs both.
  Version-neutral primitive-surface cases are carried byte-for-byte from the certified v1
  corpus; profile-bound surfaces carry v2-minted fixtures covering every v1-populated
  applicability cell, the full range-selector class matrix, and cross-major rejection
  vectors. Fixture keys were ephemeral and destroyed (provenance unchanged).
- Add v2 profiles to all four SDKs (TypeScript `v2.ts`, Python `v2.py`, Rust `v2.rs`,
  Go `v2.go`) with their own certified-digest-asserting conformance runners (Rust/Go vendor
  corpus-v2 snapshots; the sync gate covers all four snapshots) and red-capable
  permissiveness entries for the range kinds and cross-major checks.
- Add the `REQ2-*` requirement range with its MUST-to-cell map
  ([requirement map](docs/design/requirement-map.md) § v2) and six executed red proofs in
  the conformance mutation battery (`v2-selector-lte-strict`,
  `v2-selector-same-tag-removed`, `v2-selector-kinds-swapped`,
  `v2-selector-non-numeric-bound-accepted`, `v2-cross-major-grant-v-accepted`,
  `v2-request-digest-prefix-downgraded`).
- Registries: `lte`/`gte` flip reserved → active (v2); the suite table gains
  `BAP2-Ed25519-SHA256`; the anticipated ML-DSA family's index moves to the next successor
  major. The contract-major 2 activation decision record records the activation,
  the inherited-bounds review, and the named follow-ups.

### Closed — 0.4.0 publication record

- Published 2026-09-14 (registry insert 2026-09-14T06:10:54Z, source commit `5390e97` = `v0.4.0`):
  registry checksum `e6b812fa96211614608a3a8e926381856f575b9656689265ae86a1a83105a60e` equal to
  the tagged-tree two-build candidate; hexdocs live; zero requirements on the registry; a fresh
  external `~> 0.4.0` consumer resolves the release and the `V2` namespace compiles from the
  served package.

### Compatibility

- The v1 wire profile, corpus (283 verdicts, certified pin unchanged), SDK v1 behavior, and
  all v1 verdicts are byte-frozen: each major verifies under its own complete closed
  profile, v1 rejects v2 bytes and v2 rejects v1 bytes, and there is no fallback or
  downgrade in either direction. v1 is not deprecated by this activation.

### Closed — BAP-19 publication (status-record reconciliation)

- Record the 2026-08-31 owner-authorized Hex publication and registry read-back that executed
  without a tracked record: release 0.3.0 (registry insert 2026-08-31T06:08:42Z, source commit
  `c76b031` = peeled `v0.3.0`), registry checksum
  `029ee7d75641850e330d12ac41fcc24fe026f65694ad28f4ea120d871114ff89` equal to the tagged-tree
  two-build candidate, the served tarball, and a fresh external `~> 0.3.0` consumer resolution;
  served 187-file census byte-identical to the tagged tree; hexdocs live. BAP-19 closes; the
  README, getting-started guide, and Livebook installation prose now state the package resolves
  from the public registry. Zero code, wire, bound, or verdict change.

### Changed — repository hygiene

- Retire the legacy local-harness artifact tree. The tracked critical-surface declaration
  moves to the local-harness critical-surfaces path (the path the current local guards read;
  the published-governance decision record's decision stands, only the manifest location changes), and the stale
  `.gitignore` harness exception lines are removed. The four legacy project-memory
  notes are ported verbatim to the local-harness memory directory; all other legacy
  process artifacts were local-only superseded residue. No code, wire, bound, or
  verdict change.
- Record the two dependency sweeps that landed without changelog entries: `bedd1fb`
  (batched TS dev deps + actions/setup-python v7 + jsonschex 0.9.2, superseding
  Dependabot PRs #18/#19/#22/#23/#24/#26/#29) and `9ff0c34` (typescript-eslint,
  @types/node, eslint batch, superseding #31/#32/#33). Dev/tooling-only surfaces;
  no runtime dependency, wire, bound, or verdict change.
- Amend the published-governance and verifier-SDK decision records with dated relocation notes for the critical-surface
  manifest path (the legacy harness path → the local-harness path); decisions unchanged.

## [0.3.0] — 2026-08-30

### Added — byte-distinct local-loopback HTTP application proof

- Add normative profile `bap-application-proof/local-loopback-http/1`, protected
  `typ: "ba+loopback-proof"`, mandatory nonce, and exact `127.0.0.1`/`[::1]` HTTP URI rules.
- Add separate five-surface Elixir, TypeScript, Python, Rust, and Go APIs. Standard `dpop+jwt`
  functions reject the new bytes and the sibling profile rejects standard proof bytes.
- Add a secret-free certified language-neutral profile corpus plus real IPv4/IPv6 HTTP socket drill
  using fresh in-memory keys. The certified index SHA-256 is
  `10fc4cf05affcddc9e6340ff392c247e25ab038cd938f2557829a7ce63b1a5e4` (revision 1, 36 URI cases,
  8 proof cases, exact `profile.json` + `proof-cases.json` set).
- Update the README, security and usage policies, contributor guidance, installation/upgrading/
  implementer guides, all SDK guides, ExDoc extras, and the runnable Livebook. The package now
  ships the curated guides, Livebook, and ready-to-file IANA registration sources alongside the
  normative profile and corpus.

### Compatibility and distribution

- Standard v1's normative bytes and all 283 certified corpus verdicts remain unchanged. Profile
  selection is explicit and fail-closed.
- Correct three pre-release SDK conformance defects without changing the normative standard
  profile: TypeScript and Python now reject non-RFC-3986 path characters like the Elixir, Rust,
  and Go verifiers; the Go proof producer scans the grant compact before hashing it; and the Go
  standard assembler validates the complete proof payload rather than JSON shape alone.
- `v0.3.0` identifies the exact source release. Hex archive publication and registry checksum
  read-back are separate release actions; consumers must not adopt from the tag or a mutable
  checkout as though either were an immutable package identity.

## [0.2.0] — 2026-08-26

### Changed — release 0.2.0

The spec-decoupling program's public surface, released: the specification
`spec/bap-v1.md` is the single normative authority (ships in the package);
`docs/protocol-v1.md` is its generated view; the spec-facts drift gates, the
corpus tooling (six-pin digest rotation, revision sidecar, re-derivability
generator, vendored-snapshot sync), the formal model (ProVerif, P1–P4 proven),
the IANA templates, the guides set, and the interoperability report land
together. Zero wire, bound, verdict, or SDK behavior change — every entry
below is the 0.2.0 content.

### Fixed — program closeout cross-vendor review findings

The closeout review (two peer families over the full program range) returned two blocking and
several note findings, all in surfaces this program touched. All fixed:

- **[blocking] .gitleaks.toml silently disabled ALL default detection rules** — a config file
  defining only an allowlist replaces gitleaks' rule set entirely. The config now
  `[extend] useDefault = true` plus scoped, documented allowlists for the public-key-only
  fixture surfaces (the generated spec documents, the corpus/vectors trees, the vendored SDK
  snapshots, SDK test sources, and the frozen legacy-harness historical transcripts whose "PATs" are
  canonical reverse-alphabet gate self-test fakes). Verified both directions: a fake PAT in a
  scratch tree is DETECTED (3 findings) under the config, and the tracked tree scans clean
  (the only residuals are untracked gitignored build artifacts).
- **[blocking] supply-chain checksum step always failed** — `working-directory: artifacts`
  plus `cd artifacts` targeted a nonexistent `artifacts/artifacts`. The double-entry removed.
- **[note] Python/TS runners now check the revision sidecar's index entry declares zero
  cases** (Rust already asserted it) — a nonzero declaration fails all five consumers now.
- **[note] CI pin consistency** — `ocaml/setup-ocaml` SHA-pinned like every other action;
  the cddl gem checksum-pinned (`scripts/cddl-gem.sha256`) like kramdown-rfc per the A2
  amendment.
- **[note] comment anchors garbled by the spec-path rewrite** ("spec/bap-v1.md,319") fixed to
  section citations in the Python and TypeScript SDK sources.
- Residual, stated: a duplicate-member revision sidecar would pass the four SDK runners'
  permissive host JSON parsers (only the Elixir bounded decoder and the independent Node
  checker reject duplicates). The certified index SHA pin (any byte change fails all six
  pins), the corpus sync gate, and the two duplicate-rejecting consumers bound this state to
  "unreachable without simultaneously defeating the corpus identity architecture";
  duplicate-rejecting JSON parsers in four host languages for that state is disproportionate,
  recorded as accepted residual.

### Added — the successor-major charter + governance single-home + ROADMAP backfill (design-only)

- **`docs/design/successor-major-charter.md`**: the consolidated successor-major scope — delegation
  with attenuation (the delegation decision record), offline floor limits (the offline floor-limit decision record), suite succession and
  cross-suite evidence (the suite-succession and post-quantum suite records), the revocation/freshness posture (what a successor
  major MAY standardize at the interface — freshness-bearing claim shapes with stateless
  window semantics — vs what stays runtime-private permanently: live revocation lookup,
  replay reservation, trust selection, per-invocation accounting), and — per the owner's
  2026-08-26 directive — the DECIDED selector-expressiveness posture: per-request range
  selector kinds are successor-major scope (statelessly verifiable inequality kinds); a
  naive cumulative `budget` selector is explicitly rejected as a silent false security
  property, with budgets routed to issuer-attestation/runtime accounting. The parked fork in
  the AP2 mandate-mapping document is replaced by a pointer here. The absolute constants
  (stateless verifier, non-authorizing facts, closed rejection, named-suite succession) head
  the charter; an activation checklist closes it. Activates nothing.
- **Governance single-normative-home**: docs/governance.md is the one home for policy
  statements; the standards-track charter's duplicate-content claim is replaced by a
  citation — the line-cite dual-source drift class is dead. No line-number citations between
  the two remain.
- **ROADMAP backfill (additive, the public-history boundary decision record — no rewrites)**: BAP-07's closeout evidence now
  records the index-SHA-pin deferral's double closure (the 0.1.2 pin landing + the
  spec-decoupling program's six-pin one-command rotation gate and the tamper-covered corpus
  revision sidecar); BAP-18's records the authority swap's documentation catch-up (spec as
  single authority, derived view, bounds gated at all poles).
- **Vendor-neutrality sweep re-proven live**: a planted topology canary in the new tracked
  charter file reds the privacy gate ("found forbidden generic topology", exit 2) and the
  clean file passes — rule 9's mechanism covers every program-touched tracked file.

### Added — the post-quantum successor-suite statement (design-only, no wire change)

- **The post-quantum suite decision record (accepted)**: the concrete successor statement — the `BAP2-*` candidate family
  (ML-DSA-65 baseline, ML-DSA-87 higher tier; RFC 9964's JOSE mapping as the wire vehicle),
  hybrid-composite posture room reserved (draft-ietf-jose-pq-composite-sigs, decided at
  successor definition time), the quantum-readiness statement (NIST IR 8547 IPD 2030/2035
  horizon; long-lived chain/archive evidence should plan re-anchoring under cross-suite
  attestation), and the recorded bets with revisit triggers (pq-composite RFC; IR 8547
  final). Activation stays successor-major only (the suite-succession decision record); every name here remains
  reserved-and-rejected in v1 with the 283-green corpus proving it.
- **Registries + standards-track reconciled**: the anticipated-suite row now names the
  `BAP2-*` family with the post-quantum suite decision record; the charter's succession paragraph cites it.

### Added — the interoperability report (no wire change)

- **`docs/design/interoperability-report.md`**: the cross-validation evidence document for
  the frozen v1 profile — the reference Elixir verifier, the four cross-language SDKs (each
  authored from the spec + corpus alone per the verifier-SDK decision record's derivation-hygiene rule), and the
  THREE independent Node second-implementation runners (corrected count:
  corpus/chain-archive/grant-proof), each 283/283 agreed with the two-boundary key census
  two-way equal (11 keys). Framed as TEST VECTORS + INDEPENDENT CROSS-VALIDATION (the IETF
  evidence norm), explicitly NOT an implementer's list; methodology, per-implementation
  corpus bindings (in-place vs vendored snapshot, both startup-digest-asserted), and the
  reproduction path. Stated limitations: no registry publication yet (the SDK-graduation decision record), v1 only.
- **No hand numbers**: the report's certified digest (both encodings), the corpus revision
  integer, the case/surface/key counts are pinned to the LIVE corpus identity by a new
  docs-currency leg — a rotated corpus without a report update reds with the expected value
  named.

### Added — docs-currency gate + the upgrading contract (no wire change)

- **`test/docs_currency_test.exs`**: pins the public documentation surfaces to CURRENT facts —
  the changelog/version cross-reference, the four-SDK listing (case-insensitive stale-count
  check), the 283-case corpus counts, the supply-chain artifact-filename DERIVATION (no
  hardcoded versioned filename may return), the derived view's spec-revision footer, and the
  spec Doc-Revision pin shared by its formal companions. All three mutation legs executed
  red and restored: a version/changelog drift, a stale SDK count (this leg immediately caught
  a real defect — the ticket-18 README edit had landed "All four" without the Go bullet,
  now fixed), and a returning hardcoded supply-chain filename.
- **`docs/guides/upgrading.md`** (replacing its placeholder): the published compatibility
  contract — per-release-kind labels (patch/minor/major), what may never change inside v1,
  what a minor may do (corpus growth with revision bump + the six-pin rotation), the
  three-step unaffectedness check, and the deprecation pointer. The 0.1.0 RC contract remains
  the historical record.
- `/doc/` (generated ExDoc output) is confirmed gitignored — the doc/ vs docs/ hazard stays
  dead.

### Changed — housekeeping: SDK README, supply-chain derivation, dependabot, deployment guides

- **`sdks/README.md`**: the Go SDK entry and the four-SDK count (the stale "all three" is
  gone); per-SDK deployment-guide links.
- **`supply-chain.yml`**: the artifact filename is DERIVED from `mix.exs` `@version` at run
  time — the hardcoded `0.1.0` filename (a quiet-mislabel class the moment the version moves)
  is gone from the workflow; actionlint clean.
- **`dependabot.yml`**: pip (sdks/python) and gomod (sdks/go) ecosystems join mix,
  github-actions, npm, and cargo — all four SDKs now covered.
- **Deployment guides**: `docs/deployment/{typescript,python,go}-sdk.md` join the existing
  Rust guide — runtime posture, deployment targets, supply-chain posture, and the
  verification-is-not-authority boundary per SDK; the Rust guide's spec pointer repointed at
  the normative authority.

### Added — the Livebook walkthrough (no wire change)

- **`docs/livebooks/bap-walkthrough.livemd`**: the end-to-end produce → assemble → verify →
  reject notebook. It mints its OWN ephemeral Ed25519 keys at runtime via `:crypto` (zero
  tracked private material — critical rule 6; the package accepts public keys only and the
  notebook does the signing), builds a grant and proof through the deterministic producers,
  assembles both compacts, verifies the grant and the full envelope with caller-supplied
  trusted context, and then fails three classic attacks closed (a tampered signature byte, a
  selector-disallowed argument swap, a stale proof outside the max-age+skew window). The
  notebook's cells were executed VERBATIM (extracted and run as a script): all three attacks
  return exactly `{:error, :invalid}`. Two authoring bugs found and fixed by that verbatim
  run: a mis-grouped expiry literal and a `defp` helper cell that cannot exist at Livebook
  top level.
- **`test/bap_walkthrough_test.exs`** (the notebook's mirror): re-executes the walkthrough's
  exact code shape against the real package — if the notebook's code stops running, the test
  fails (a non-running livebook is worse than none) — and a shape-parity leg pins the
  notebook's code markers to the test (mutation leg executed: breaking the notebook's code
  shape reds with the marker named; restored green).

### Added — corpus vendored-snapshot sync gate (no wire change)

- **`scripts/check_corpus_sync.exs` + `mix corpus.sync` (quality leg)**: byte-compares the
  certified corpus (`priv/conformance/v1/corpus/`) against BOTH vendored SDK snapshots
  (Rust, Go) — every file, both directions, content equality. A snapshot drift is caught at
  COMMIT time instead of as a red SDK suite or a confusing digest rotation. Within the corpus-distribution decision record
  (an intra-repo drift check, not a distribution artifact). Mutation leg executed: a flipped
  byte in a vendored revision.json reds with the file named; restored green.
- The CI quality job already runs `mix quality`, so the new leg is enforced there without a
  new workflow.

### Added — the implementer's guide (fifth-SDK recipe; no wire change)

- **`docs/guides/implementers-guide.md`** (replacing its placeholder): everything a fifth
  implementer needs without reading a sibling runner's source — the conformance-runner
  contract (startup corpus-identity assertion, full integrity verification, total verdict
  comparison: crashes are failures, never skips); the 28-surface dispatch table with each
  surface's role; the two-boundary key census (discovery == verify-import == index set,
  and the two runner cheats it catches); the corpus formats including the revision sidecar;
  the permissiveness-mutation expectation with the six language trap classes
  (duplicate-member rejection, null-prototype equivalence, the raw-lexeme ceiling,
  single-value/trailing, the integer/float tag distinction, decode-depth scalar asymmetry)
  each required red-capable in the implementer's own language; and the facts-anchor contract
  for embedding the spec.
- **Acceptance leg**: `test/docs_guides_test.exs` cross-checks the guide's dispatch table
  against the LIVE corpus surface enumeration, both directions named on drift (a corpus
  surface without a guide row, or a phantom guide row, reds).

### Added — code of conduct, guides taxonomy, README restructure (no wire change)

- **`CODE_OF_CONDUCT.md`** (Contributor Covenant v2.1, canonical text) at the root, linked
  from CONTRIBUTING.md.
- **Guides taxonomy** under `docs/guides/`: getting-started.md (zero to a verified envelope,
  the three rules that surprise newcomers) and a curated-order README; wired as ExDoc extras
  in reading order (implementers-guide and upgrading land with their own slices as wired
  placeholders).
- **README restructure**: a Documentation section leading with the guides index; a "When NOT
  to use this" section (authorization decisions, transport/confidentiality/storage,
  permissive parsing, online revocation/replay — each pointing at what to use instead); and a
  Named-misuses section (facts-as-authority, facts-as-runtime-input, kid-as-key-selector,
  bound-loosening, client-derived request context) — the ash_onetime anti-pattern format,
  product-neutral.

### Added — ProVerif model + CI harness + findings ledger (no wire change)

- **`spec/formal/proverif/bap-core.pv`**: the stateless-core model under ProVerif 2.05 —
  Dolev-Yao network attacker plus a malicious holder with a fully-controlled signing oracle;
  the typed projection and compact framing as free constructors (the recorded symbolic
  assumptions); every verifier step annotated with its REQ id. **All four properties PROVEN
  TRUE**: P1 (a proof binds exactly the `ath`-named grant — the verifier event carries the
  digest of the grant IT was presented), P2 (request-digest equality with the verifier-derived
  pair), P3 (context-tuple equality), P4 (the server-derived cast arguments stay secret — the
  facts output constructor takes only declared fields).
- **Model non-vacuity proven by mutation**: removing the `ath` check makes P1 unprovable; the
  strengthened query and per-issuance grant freshness were themselves the ledger's first
  (refuted) finding — the initial P1 was too weak to see cross-grant replay.
- **`scripts/run_formal.sh` + `mix formal` (quality leg) + a CI job**: pinned ProVerif
  re-run with the RESULT-line diff against the frozen `expected-summary.txt` (a
  verdict-changing model edit reds — leg executed), REQ-id coverage with a pinned step-
  annotation set (dropping an annotation reds — leg executed), and the findings-ledger schema
  check (an undispositioned entry reds — leg executed). An escalated disposition prints
  loudly and blocks the program done-claim (the formal-analysis decision record).
- **`spec/formal/FINDINGS.md`**: the ledger, one terminal disposition per finding
  (refuted/documented/escalated); first entry dispositioned refuted; none escalated.
- The model joins the rule-8 companion pin (spec Doc-Revision); `spec/formal/` ships in the
  package; census extended exactly.

### Added — formal attacker model + formal-analysis decision record + gate rule 8 (no wire change)

- **`spec/formal/attacker-model.md`**: the formal attacker — Dolev-Yao network attacker plus a
  malicious holder attempting cross-grant/cross-operation/cross-endpoint/cross-invocation
  confusion — and the four modeled properties: P1 (a proof binds exactly the `ath`-named
  grant), P2 (the request digest binds the verifier-derived operation + typed arguments
  exactly; typed-projection injectivity is the load-bearing lemma), P3 (context-binding
  completeness across method/URI/invocation/nonce, with time abstracted symbolically and the
  assumption recorded), P4 (facts disclose nothing beyond declared fields). Scope and
  symbolic assumptions stated; self-contained and vendor-neutral; pinned to the spec's
  Doc-Revision.
- **The formal-analysis decision record (accepted)**: the formal-analysis program — ProVerif chosen for automation fit on
  the stateless core (decision recorded as decided-with-open-evidence: no published
  machine-checked DPoP-family model exists; FAPI 2.0 CCS 2024 defers formal models), Tamarin
  named follow-on with a recorded trigger (first chain/archive ordering or timepoint lemma
  need), the standing CI posture, and the three-valued findings-ledger disposition contract —
  `escalated` routes to the owner via SECURITY.md and blocks the program's done-claim until
  the owner rules; the coupling is stated, not hidden.
- **Spec-facts rule 8**: every spec/formal companion names the revision it was written
  against; a spec revision bump without the companion (or vice versa) reds with both values
  named. Mutation leg executed: a lone spec rev bump reds, restored green. (The ProVerif model
  joins the companion list at its own landing.)

### Changed — docs/protocol-v1.md becomes a generated view; repoint census; spec-decoupling record

- **`spec/tools/render_derived.exs` + `mix spec.render` (quality leg)**: the package-facing
  `docs/protocol-v1.md` is no longer authored — it is generated from the normative
  `spec/bap-v1.md` (front matter stripped, citations normalized, generation footer naming the
  authority and revision). Hand edits are gate-red; the write is idempotent.
- **Repoint census**: the living normative citations now point at the spec — README, the
  requirement map, registries, standards-track charter, conformance contract, and the four SDK
  sources' docstrings (comment-only; zero behavioral SDK change; all four suites re-verified
  283/283 + census). Historical records (decision records, roadmap rows, errata) keep their
  closure-time citations as history.
- **The chain-and-archive decision record** gains the supersession note (the spec is the normative home of the byte
  definitions; the record remains the decision record). **The spec-decoupling record** (accepted) records the
  decoupling decision: the single-authority swap, the certification-not-trust posture, the
  derived view, the durable-identity decision record same-major-document-relocation amendment, toolchain provenance
  (kramdown-rfc 1.7.40 version+checksum-pinned, cddl 0.12.14, both executed locally), and the
  I-D-shaped-readiness venue posture.
- **Package census**: `spec/` ships in the package (the specification, its CDDL, its facts
  baselines, and its three generator tools); `check_package.exs` @expected_files extended
  exactly; the architecture-gate package boundary green.
- **The one-time full comparative read** of the new spec against the old authority was
  performed with written dispositions (mechanical parity: 76/76 REQ ids, 37 bounds rows,
  byte-identical constructions; one real gap found and fixed — the frozen façade listing now
  restored verbatim into informative Appendix C). Disposition record: the swap's review
  artifact; the empty-delta certification continues to run in `mix quality`.

### Added — generated worked examples (spec Appendix A; no wire change)

- **`spec/tools/build_examples.exs` + `mix spec.examples` (quality leg)**: Appendix A of the
  specification is now GENERATED from the conformance corpus — thirteen representative cases by
  id (a valid grant/proof/envelope/chain/anchored-export per surface family, plus rejected
  exemplars across the invalid-class families), each embedding the exact compact bytes, the
  public key material, and the single expected outcome (accepted-with-redacted-facts or the one
  closed error), labeled with its corpus case id. Corpus PUBLIC keys only — no private material
  can appear (the corpus is public-only by construction and the tool copies verbatim).
- The regeneration gate reds on a hand-edited example byte (mutation leg executed: flipped
  compact byte caught, restored) and on a deleted/renamed cited case id (mutation leg executed:
  the rebuild raises naming the missing id, restored) — the appendix cannot drift from the
  corpus and cannot go vacuous.
- The write path is idempotent (byte-identical across consecutive regenerations); the
  empty-delta certification is untouched (the appendix carries no facts anchors and no
  capitalized keywords).

### Added — Security and Privacy Considerations + spec-facts rule 11 (no wire change)

- **Spec section 21 (Security considerations, RFC 3552 grade)**: assets and goals (integrity,
  not confidentiality — every wire object is plaintext by design), adversaries and the exact
  trust boundary (raw bytes in, redacted facts out), the per-control mapping to REQ ids
  (parsing-before-crypto, exact-byte constructions, holder/grant/request bindings, temporal
  and nonce semantics, authenticated transitions, complete-scan archive acceptance),
  verification-is-not-authority stated as a security property, explicitly out-of-scope
  operational controls, and residual risks (in-window replay, compromised issuer, correct-but-
  wrong trusted keys, malicious archive controllers, implementation error). Self-contained;
  the threat model, charter, and extension security sections remain as extended records.
- **Spec section 22 (Privacy considerations, RFC 6973 shape)**: the correlation surfaces
  (`jti`, `ba_inv`, `aud`, issuer identifiers), chains and archives as deliberately durable
  longitudinal evidence with the retention-policy tension stated, nonce linkability and the
  optionality-as-privacy-feature note, and redacted facts as the profile's loudest privacy
  control. Deployment recommendations carry NO capitalized RFC-2119 keywords.
- **Spec-facts rule 11 (keyword census)**: every capitalized MUST/MUST NOT/SHOULD/SHOULD
  NOT/REQUIRED/SHALL/SHALL NOT in the spec is either a quoted mention, or shares its sentence
  with a `REQ1-*` id, or sits under an explicit informative marker — a naked keyword in
  unanchored prose reds with the sentence quoted. Mutation leg red-quoted (planted naked MUST
  caught) and restored; every mapped id exists in the requirement map (rule 3).

### Added — IANA registration templates + spec-facts rule 7 (no wire change)

- **`docs/design/iana/`**: the machine-readable registration sources and rendered ready-to-file
  markdown for the profile's IANA entries. JSON Web Token Claims Registry (its published
  four-field format, Specification Required): `ba_inv`, `ba_op`, `ba_req` ready to file;
  `ba_dlg`, `ba_offline`, `ba_sut` reserved-marked and NOT filed. Media types (RFC 6838
  section 5.6 field template, LIMITED USE): `application/ba-cap+jwt`,
  `application/ba-chain-anchor+jwt`, `application/ba-key-transition+jwt` ready to file;
  `application/ba-cap-delegated+jwt`, `application/ba-suite-attestation+jwt` reserved. The
  drafted `ba+*` wire `typ` values are NOT registrable as-is (`+cap` is not a registered
  structured suffix; `+jwt` is) — **the wire `typ` values are unchanged**; the media-type
  namespace is distinct. Filing is externally gated (the BAP-08 official-submission
  preconditions); the templates make in-repo readiness verifiable.
- **Spec §IANA considerations** (section 20): the registration requests, the reserved names,
  and the namespace distinction, per RFC 8126 guidance (read first-hand).
- **registries.md typ table gains the media-type column**; purposes are now byte-exact shared
  facts between the registries document and the IANA sources.
- **Spec-facts rule 7**: registries.md == IANA template sources, exact names/statuses/purposes
  both directions, with two documented allowsets (`ba_obo`: reserved without a template by
  design; `dpop+jwt`: registered by RFC 9449) — BAP-12's acceptance criterion mechanized.
  Mutation leg proven red (flipped template status caught, named) and restored.

### Added — the v1 specification `spec/bap-v1.md`; authority swap certified with an EMPTY delta

- **`spec/bap-v1.md`** (kramdown-rfc conventions, `[@RFC2119]`-style citations, no IETF
  stream/IPR title-block fields — in-repo readiness, zero submission intent): the complete
  normative v1 profile in one language-neutral document — conformance language and the
  closed-rejection invariant, suite identity, normative references without repo paths, the
  abstract tagged data model and typed projection, JSON decoding and JCS serialization,
  base64url, wire objects (headers, claims, selectors, URI normalization), signing and digest
  inputs with the `BAP1-*` domain separators, the consumption chain / boundary anchors /
  authenticated key transitions / anchored export framing folded NORMATIVE from the chain-and-archive decision record, the
  public verification contract as algorithm prose (the Elixir bindings and frozen façade moved
  to informative Appendix C), hard maxima, the untrusted key locator, and the `typ` registry.
  Informative appendices: B the CDDL summary, C the reference mappings and tagged-algebra
  binding table, D the requirement-inventory pointer.
- **The authority swap is certified**: the spec-facts extractor now reads `spec/bap-v1.md`
  (all ten anchors in the one normative document) and `extract(new spec) ==
  spec/facts/baseline-v1.json` byte-for-byte — the frozen pre-swap extraction, EMPTY delta.
  `docs/protocol-v1.md` keeps its anchors but is no longer read (the derived view lands with
  its own landing); the chain-and-archive decision record and registries.md remain extended records.
- **Framing oracle** (new spec-facts rule): a corpus archive-bearing valid case's real bytes
  are re-parsed against the EXTRACTED framing facts (archive prefix, UINT32_BE nonzero-length
  frames, exact EOF) — a spec whose framing prose drifts from accepted bytes reds even when
  the delta gate happens to pass. Proven red twice during construction (wrong prefix form,
  wrong chunk decoding) and green on the shipped corpus.
- **`spec/cddl/bap-v1.cddl`** (informative): the closed wire objects in CDDL, honest about
  CDDL-inexpressible invariants (duplicate names, canonical bytes, tagged integer/float,
  binary framing). **CI coherence job** (`scripts/check_cddl_coherence.rb`, pinned cddl
  validator 0.12.14, executed locally at authoring): every valid corpus case's CDDL-covered
  artifact validates (grant/proof/anchor/transition payloads, consumption rows), and four
  PINNED invalid exemplars (regexp/size/closed-set violations) reject — the pinned list cannot
  go vacuous. A pinned-and-checksummed kramdown-rfc 1.7.40 render job renders the spec to
  xml2rfc v3 (verified locally: renders clean).
- The spec-facts battery entries re-anchored at the spec (a digit drift or deleted anchor in
  `spec/bap-v1.md` reds). `mix spec.facts` green against the NEW authority in ~0.1s.

### Added — spec-facts mutation battery (no wire, bound, or verdict change)

- **`scripts/check_spec_facts_mutations.exs` + `mix spec_facts.mutations` (wired into
  `mix quality`)**: the spec-facts gate's own red-capability battery, mirroring the conformance
  mutation-gate doctrine — scratch-copy isolation, one anchored source mutation per entry,
  baseline-green non-vacuity (a deleted target can never score as "caught"), and one
  inverted-assertion calibration self-proof executed at authoring (under a neutered rule-1
  assertion the bounds mutation SURVIVES green — the exact condition the battery raises on).
  Seven entries, each proven red in-landing: spec bounds digit, live Bounds digit, requirement
  statement softening, cited-count drift, revision-citation drift, deleted anchor, and a
  renamed optional-unobserved coverage mark (the marks file is load-bearing). The privacy-canary
  mutation stays outside the battery by design — the canary sweep needs the real git worktree
  and carries its own calibration tests.

### Added — spec-facts drift gate against the current authority (no wire, bound, or verdict change)

- **`spec/tools/extract_facts.exs` + `spec/facts/baseline-v1.json`**: the normative facts of the
  CURRENT authority (`docs/protocol-v1.md` + the chain-and-archive decision record byte definitions + the registries typ
  table) are now machine-extracted from ten closed `<!-- facts:key -->` anchor regions (bounds,
  header members, grant/proof claims, selector kinds, typ values, domain separators, digest
  constructions, archive framing, error shape) and frozen as a byte-deterministic baseline. A
  future editor who changes any normative fact — spec, bound, corpus, requirement map, or
  registry — is stopped by `mix spec.facts` naming the divergent pole pair.
- **`scripts/check_spec_facts.exs` + `mix spec.facts` (wired into `mix quality`)**: rule 1b
  (extraction == frozen baseline), rule 2 (spec closed sets ⊇ the corpus's valid-case member
  unions, direction-aware, with justified optional-unobserved marks in
  `spec/facts/coverage-v1.json`), rule 3 (spec REQ-id set == requirement-map set, each defined
  once), rule 4 (requirement-statement hashes == `spec/facts/requirement-statements-v1.json` —
  silent softening reds), rule 5 (map-cited counts re-derived from the live corpus index),
  rule 6 (map cites the corpus revision integer), rule 9 (every authority file git-tracked and
  thus inside the public-surface privacy gate's full-tree canary sweep — the public-history boundary decision record topology;
  no term list is tracked), rule 10 (anchor completeness: every normative table inside exactly
  one anchored region, with the JSON-algebra binding table's coverage mechanically verified
  against the typed-projection facts). Rule 1's bounds-dump equality lives as
  `test/spec_facts_test.exs` (needs the compiled Bounds module).
- The gate ships GREEN against the old authority — the guards are proven against the world they
  protect BEFORE the authority swap. Every rule proven red-capable at landing by a named
  mutation (spec digit, Bounds digit, map statement softening, cited count, revision citation,
  a planted privacy-canary term in an authority doc, a deleted anchor — each caught, each
  restored). Gate runtime ~0.2s; rules 7 (IANA), 8 (formal companions), and 11 (keyword census)
  arrive with their owning landings.

### Added — corpus revision sidecar + re-derivability generator (no case byte or verdict change)

- **`priv/conformance/v1/corpus/revision.json`**: the corpus's monotone revision integer (1)
  with a generated-from provenance note, hash-covered as an entry in `index.json`'s per-file
  SHA `files` set and enforced by exact file-set equality in every consumer — a tampered,
  deleted, malformed, or wrongly-shaped sidecar fails every loader closed. `index.json` bytes
  change once (the single files entry; 283 cases, verdicts, counts, and applicability are
  byte-identical). The corpus-index schema's file-path pattern admits exactly the one reserved
  root path. All six certified-index-SHA pins rotated in the same commit via the regeneration
  script (the corpus-distribution decision record atomic-landing template), and both vendored SDK snapshots re-copied.
- **`conformance/generators/`**: the corpus re-derivability tooling (authoring tooling, not a
  runner, not in the published package). `build_corpus.mjs --verify` proves the shipped corpus
  equals a rebuild from its frozen case files plus the shipped curated inputs (n_a reasons +
  the public-key fingerprint census), including re-derivation of every tamper case's verbatim
  artifact; `--rebuild-index`/`--bump-revision` are the amendment path. The README records the
  honest provenance: the signed fixtures were minted with ephemeral keys at authoring time and
  cannot be re-minted; everything derived is machine-rebuilt and byte-verified.
- Every corpus consumer recognizes the sidecar fail-closed (Elixir loader + CLI, the
  independent Node runner, and the TypeScript/Python/Rust/Go SDK runners — the two loaders that
  previously skipped case-free files silently now enforce the sidecar's SHA and closed shape).
- `docs/design/requirement-map.md` cites the revision integer (the `format`-string citation
  was constant across revisions and could detect nothing); the five consumer-doc digest
  citations refreshed to the rotated values.

### Added — corpus digest regeneration gate (no wire, bound, or verdict change)

- **`scripts/regen_corpus_digests.exs` + `mix corpus.digests`**: one command regenerates every
  certified corpus index-SHA machine pin — the four SDK conformance runners in their native
  encodings (base64url: TypeScript, Go; hex: Python, Rust) plus the two Elixir pins (the CLI's
  fail-closed certified-corpus assertion and its test mirror) — and the check leg, wired into
  `mix quality`, fails red the moment any pinned constant drifts from the live `index.json`
  digest (corrupt-one-constant and scratch-index-byte mutation legs proven red at landing). The
  Elixir-side pin itself landed in the 0.1.2 hardening; this closes the tooling gap the BAP-07
  gate-integrity review recorded and makes every future corpus rotation a single-command,
  same-commit affair (the corpus-distribution decision record atomic-landing template). No corpus byte changes in this landing:
  all six constants keep their current values, and `--write` is byte-idempotent today.

### Fixed — public-history privacy boundary

- Public documentation and reachable Git history no longer identify private product repositories
  or their deployment topology. The public-history boundary decision record records the durable boundary and the all-ref rewrite.
- `bounded_authority` is explicitly a private commercial application: it must never be published
  to public Hex, is not currently distributed through private Hex, and any future private-Hex
  release requires both a paid subscription and fresh owner approval for that exact release.

### Fixed — v1 selector contract reconciliation

- The protocol and both shipped selector schemas now describe the released `all` behavior: `all`
  accepts any of the three recognized member sets and treats `path`/`value(s)` as inert. Go,
  TypeScript, Python, and Rust now reject every other member combination, matching the Elixir
  reference and independent runner. The `all`-selector erratum decision record records the zero-verdict-change erratum.

### Added — Go verifier SDK (BAP-16; no Elixir package change)

- **`sdks/go/` — the typed Go verifier SDK** for the frozen v1 profile, authored from the spec +
  decision records + conformance corpus alone (the verifier-SDK decision record): the 17-function façade plus versioned primitives,
  zero runtime dependencies (stdlib `crypto/ed25519` + `crypto/sha256` only), Go floor 1.25. Passes
  all 283 conformance vectors from a vendored corpus snapshot with startup index SHA-256 assertion
  and the two-boundary key census (`agreed=283 disagreed=0 census=11`). The per-language
  permissiveness battery ships with per-clause red-capable legs for every closure and gate (no F1
  debt: 14 mechanical mutation probes proven RED at authoring), including the inter-SDK contract decision record's five clauses
  (closed Result panic guard, pre-digest hoist with a zero-hash work pin via the internal
  `archiveDigest` seam, canonical byte-equality, signature width at decode, role-bounded frame
  reads) and the SDK bounds contract decision record's bounds threading incl. `assemble_compact` caller limits and nested-pins
  identity semantics. Purity vet + zero-dependency license gate (each red-capable) and a
  `go-conformance` CI job. Not in the Hex `files:` list; no registry-publish infrastructure
  (the SDK-graduation posture).
- **Cross-vendor hardening (cross-vendor review, fix pass `fbff228`):** proof-claim presence
  tracking, export anchor-chain cross-binding, standalone genesis zero-hash, port-overflow
  closure, embedded-IPv6 group counting with dotted-form preservation, post-decode host
  classification with lowercased decoded bytes, tightened integer-magnitude enforcement,
  UTF-8 object-version validation, chunk-vs-byte bound separation at encode, a digest-deriving
  export producer, proof-producer nonce support, constant-time nonce comparison, and
  runner/purity-gate hardening — each behavioral fix pinned by a red-capable
  review-regression leg.

## [0.1.2] — 2026-08-20

### Changed — release/verification gate hardening (no wire, bound, or verdict change)

- **`mix conformance.verify` pins the certified corpus index SHA-256** (the verifier-SDK decision record). The Elixir
  verifier CLI now fails closed unless the loaded corpus is the exact certified snapshot — closing
  the gap where a self-consistent but shrunken corpus (regenerated index) passed integrity and
  agreement. Parity with the three SDK runners, which already pin the identical value.
- **The architecture gate asserts every pinned beam is present** (keys-⊆-present). A pinned public
  surface deleted or renamed without updating the allowance now reds the gate instead of vanishing
  silently.
- **The SDK publish guard scans SDK scripts, Makefiles, justfiles, and composite actions**, not
  only workflows and top-level manifests — a registry-publish command can no longer hide in an SDK
  release script or a composite action.

## [0.1.1] — 2026-08-20

### Added

- **Ship `priv/conformance/v1/vectors/` in the Hex package.** The four named vectors
  (`grant-holder-proof`, `chain-semantic-edge`, `consumption-chain-archive`, `manifest`)
  are the acceptance oracle a holder-side consumer verifies its envelope production against
  (the capability-authorization extension decision record's corpus-as-oracle posture). 0.1.0 shipped `corpus` + `schemas` but not
  `vectors`, so a Hex consumer could not reach the oracle vector; 0.1.1 ships it. Public
  test data, same class as the already-shipped corpus — no private material (verified: the
  manifest carries only discovery roots and public-key fingerprints). The exact-file
  package census is extended to match. Zero wire byte, bound, or verdict change.

## [0.1.0] — 2026-08-20

### Added

- **First public release (BAP-07, executed 2026-08-20 by owner decision).** The exact reviewed
  candidate published to Hex as `bounded_authority_protocol` 0.1.0. Connected verification: the
  private runtime's PostgreSQL 18 gate passed 996/996 against the pinned candidate, and the
  consumer's gates passed 819 tests including the immutable authority-contract bundle's 15
  consumer cases; the opt-in live-endpoint consumer gate was provisioned and run for this
  closeout. Fresh correctness, security, and gate-integrity reviews closed with their findings
  fixed in this landing; the cross-vendor peer returned no findings, and the third-family
  lens was a named sensitivity-policy skip. The publication sweep in this landing: the
  private-strategy links left the README, every unpublished/deferred claim in the shipped docs
  became the published truth, the release-candidate decision record gained the 2026-08-20 amendment lifting the deferral, the
  consumer-seams design note left the package and hexdocs, the mutation batteries gained
  baseline-green runs (an entry now fails if its target test cannot run green unmutated), the
  bounds-aware facade gained widening/malformed-bounds rejection legs, and `.gitignore`/dependabot
  gained the public-repo hygiene the security review named. The `v0.1.0` tag moved to the
  published commit so hexdocs `source_ref` links resolve.

### Fixed

- **BAP-07 readiness reconciled across the repo docs.** The ROADMAP row's acceptance still named
  the retired private-runtime PG 16/17/18 matrix (a private-runtime decision made PostgreSQL 18
  the sole supported major); the ROADMAP Next-action note, the release-candidate contract's Status,
  the README Status, and SECURITY.md's supported-versions note each described BAP-07 as gated on the
  private runtime's connected gates —
  private BA-14 completed 2026-08-18, so BAP-07 is fully unblocked (the Hex-publication half stays
  deferred by maintainer decision). Docs-only: zero code, wire, bound, or verdict change.
- **Cross-vendor review round 18 (blocking + should-fix/notes across the vendor lenses, all
  closed).** Blocking findings: the TS shape gate's nested expected-export members were opaque `"object"`
  specs, so a malformed nested struct (chain missing `previousHash`, empty anchor) passed the
  gate and the clause-3 hoist then derefed the missing field — a `TypeError` escape past
  `trying()`; the nested members are now fully specified (chain/anchor/transition field shapes,
  `archived.chunks` as a bytes sequence), red-leg + mutation proven (opaque specs → RED).
  Should-fix findings: the Python and Rust hoists walked `expected.transitions` per-element BEFORE the
  `key_transitions` count ceiling, regressing the round-3 ceiling-first invariant the TS sibling
  kept; the ceiling now runs at the top of the hoist in both (the dead later duplicate removed).
  Notes, reconciled: the entry-position shape gate's sequence walks before count
  ceilings are a documented accepted margin (typeof-only per element); Python's body-level
  `bytearray` tolerance was dead under the exact-bytes gate and is reconciled to bytes-only.
  The timed-out fable peer's transcript was mined: it was converging on the ordering finding
  (fixed) and an API-shape note that is benign (the SDKs' optional bounds default to maximum —
  behavior-identical to the reference's public façade).
- **Round-12..14 per-clause pin debt paid on the touched surfaces (the inter-SDK contract decision record's Honest limit,
  amended).** The 2026-08-18 cluster's legs double as the owed per-clause pins: the pre-digest
  export gates (version shape/equality, key-count, key-id charset, key magnitude) are now pinned as
  WORK legs in the Python battery (reject with zero sha256 calls — all five mutation-proven
  load-bearing) with TS/Rust verdict matrices. Root cause of the "unpayable" debt: each gate is
  verdict-subsumed by a later gate, so verdict legs were structurally impossible; the work form is
  the red-capable pin. Still owed and disclosed: the standalone anchor-path `anchor_bytes` gates
  (round 12) and allocation bounds (round 15) on untouched surfaces.
- **The SDK bounds contract decision record's named `assemble_compact` divergence closed: caller bounds threaded through
  assembly in all three SDKs.** The reference takes limits at assemble (`runtime.ex:147-155` →
  `CompactJws.assemble` — encoded-segment bounds, compact_bytes, and the kind re-parse against
  `Bounds.coerce(limits)`); the SDKs hardcoded maximum. Each SDK now takes an optional bounds
  parameter (Python `bounds: Bounds | None = None`, TypeScript `bounds?: Bounds`, Rust
  `bounds: Option<&Bounds>` — an additive public-API change; absent bounds = maximum, backward
  compatible) and threads it through the reference's gates. `signature_bytes` carries no
  assemble-time gate — fixed-width at Bounds construction (the reference's check is subsumed).
  Mutation-proven per SDK (reverting the threading reddens each battery's tightened-bounds legs).
- **The inter-SDK contract decision record exception 2 closed: the expected-anchor identity ordering divergence (all three
  SDKs).** The reference validates the expected struct (chain + both anchors' identity/binding
  well-formedness + transitions) BEFORE hashing the archive chunks (`anchored_export_codec.ex:88-104`);
  the SDKs ran those gates only post-digest. Verdict-invariant by subsumption — the fix restores the
  clause-3 work ordering: malformed caller metadata now rejects without hashing the archive. The
  Python battery proves it behaviorally (sha256 call-count == 0 on malformed-expected rejections,
  mutation-proven); TS and Rust pin the ordering structurally (hoist block before the digest site,
  mutation-proven) plus verdict-matrix legs.
- **The inter-SDK contract decision record exception 1 closed: the SDK closed-Result escape family (Python + TypeScript).** A
  mechanical family sweep (every façade × every parameter × wrong-typed values, then every struct
  field the same way) proved the 2026-08-17 ledgered Python escapes were not two instances but a
  total class: 31/34 Python parameter sites and every caller-supplied struct field raised
  `AttributeError`/`TypeError` past the closed `Result`, and TypeScript — whose coercion the record
  had classed as a "disclosed margin" — silently accepted `requestDigest(123/true/null/{})`
  (digesting the coerced text) and threw `TypeError` past `trying()` on 109 parameter positions.
  Both dynamic SDKs now gate every façade argument's shape before the body runs (Python:
  annotation-driven `_closed_shape` over the declared dataclass shapes, `bool` ≠ `int`; TS:
  `closedShape` with per-façade shape specs). Rust excludes the class by typing. The per-SDK
  family-sweep batteries in `tests/test_permissiveness.{py,ts}` are the pre-fix red run and are
  mutation-proven per façade. Exception 2 (expected-anchor identity post-digest) remains open.

### Added

- **Bounds-aware public compact assembly and current-major issuer posture.** Add the public
  `BoundedAuthorityProtocol.V1.assemble_compact/3` facade over the existing bounded runtime
  primitive; `/2` remains exactly `/3` with profile maxima. Native tests cover byte equality for
  grant, proof, boundary-anchor, and key-transition compacts; tightened encoded-segment and final-
  compact rejection; and continued rejection of `ba_dlg` / `ba+cap-delegated` in current v1.
  The assembly-bounds decision record records that
  a private authority may issue an independently valid, narrower current-v1 grant to a new holder,
  while lineage remains runtime-only and portable holder-signed delegation remains successor-major.

- **The SDK contract decision records (BAP-15 documentation slice).** Three new accepted records capture contracts
  that had shipped as code + ROADMAP evidence amendments only: the inter-SDK contract decision record
  (the inter-SDK behavioral contract the 17-round cross-vendor hardening arc converged on — closed
  Result surface, type strictness, pre-hash validation, canonical-form byte equality +
  signature-width gates, role-bounded frame reads), the SDK bounds contract decision record
  (the caller-tightenable bounds contract through the expected structs, including the
  nested-pins identity semantics and the named `assemble_compact` maximum-bounds divergence), and
  the corpus-distribution record (the SDK-graduation record's deferred
  corpus-artifact question settled: per-SDK binding until the first SDK graduation, on the traced
  two-SDK bump-amplitude evidence). Fact corrections in the same landing: the verifier-SDK decision record's Node floor
  corrected to >= 22 (raised at `d9df0bf` on Node 20 EOL) and its `@noble/curves` "optional
  browser-build path" claim withdrawn (never present in `sdks/` history); the release-candidate decision record's BAP-07
  "publishes the exact candidate" consequence annotated with the maintainer's Hex-publication
  deferral (the `v0.1.0` git tag at `c65d3be` is the internal pin). Zero wire-behavior change. The
  inter-SDK contract authoring review (cross-vendor) surfaced two verified SDK contract exceptions, disclosed
  as the inter-SDK contract decision record's named exceptions and routed as SDK-code fixes: a Python closed-Result escape
  (non-string `request_digest` operation / `ConsumptionEntry.chain_id` raises `AttributeError` past
  the façade), and all three SDKs validating the expected-anchor identity fields post-digest where
  the reference validates them pre-digest.

- **BAP-09 SDK conformance hardening (TypeScript + Python).** The TS + Python verifier SDKs now
  enforce two checks the Elixir reference has and the SDKs were missing: (1) a decoded
  **signature-width gate** in `parseCompact`/`parse_compact` (`len(signature) == signature_bytes`,
  mirroring `runtime.ex:237`/`:259` — `scanCompact`/`scan_compact` intentionally stays shape-only,
  mirroring `CompactJws.scan`); (2) **canonical-form equality** for boundary-anchor + key-transition
  compacts — the protected header AND payload segments must equal the exact JCS re-encoding
  (mirroring `boundary_anchor_codec.ex:95-96,118-119` + `key_transition_codec.ex:127-128,151-152`),
  so a non-canonical encoding (e.g. reordered members) is rejected. New red-capable tests cover the
  anchor + transition canonical paths (header + payload) and the signature-width gate. Verified: TS
  92 unit + conformance 283/283; Python 62 unit + conformance 283/283. (The Rust/Go SDKs — BAP-15/16
  — may carry the same gaps; a separate check is owed.)

- **All-SDK pre-hash validation hardening (BAP-15, cross-vendor rounds 11-15).** The archive
  verify paths across Rust/TS/Python now validate the full caller-context shape BEFORE the
  digest: the object versions (string, non-empty, UTF-8 bytes <= 512, well-formed, equal),
  the key chain (exact count, key-id ASCII-unreserved class + width), key windows
  (integral + magnitude + ordering), and identifier well-formedness — malformed metadata
  no longer forces maximum-sized hashing, ill-formed strings fail closed in every SDK
  (Python's UnicodeEncodeError escape closed; TS's silent U+FFFD replacement closed), and
  frame reads are role-bounded per chain_row_bytes/anchor_bytes.

- **Result-contract fail-closure + ChainInput type strictness (BAP-15, cross-vendor rounds 16-17).**
  Every path where a caller-supplied Python value could raise out of the closed Result API
  now fails closed instead *(2026-08-17 delta: "every" was overclaimed — two further escape paths
  were found by the inter-SDK contract review and routed; see the SDK contract records row below)*: ill-formed
  or non-str identifier strings (`_utf8_bytes` gates
  chain_ids, key_ids, versions, and every expected-side string the header construction
  encodes), non-int chain integers (gated before the sequence arithmetic), non-bytes chunk
  elements, and Boolean ChainInput integers (True == 1 no longer verifies — TS's strict
  equality and Rust's typing already rejected them). The TS chunk-type sibling gate added
  for family symmetry; the check_chain chain_id StringOrURI shape validation that only
  Rust carried is now in all three SDKs.

- **Rust SDK bounds parity (BAP-15; closes the LAST named delta).** Caller-tightenable
  bounds through the expected structs — the exact reference/sibling shape: five additive
  `Option<Bounds>` fields (None = the profile maximum), the nested-bounds pins with identity
  semantics (a present nested must coerce-equal the outer; an absent nested is valid only under
  an effectively-untightened outer — identity overrides are NOT tightening), and the resolved
  bounds threaded through every ceiling at encode AND verify + the standalone chain/anchor/
  transition entries. 27 mutation-proven legs at closeout (permissiveness 39 → 67 — the original 12 plus
  the five the four closeout lenses forced: the chain_rows count, the standalone
  transition, the verify-pin family on a REAL corpus-signed archive, and the two
  encode-pin isolations; the standalone anchor leg, the chunk-count MAGIC pin now reachable and landed, and a 7/7
  Elixir oracle receipt (local re-runnable oracle artifact). `assemble_compact` stays at maximum
  (the siblings too). (Scope driver: the 2026-08-14 maintainer direction — direction input,
  not this entry's verdict.)

- **TypeScript + Python encode-path validation parity (BAP-15).** Both sibling SDKs gain the
  same producer contract the Rust SDK just closed: a full `checkChain` re-check of the rows, and
  gated parses + 7-field matches for the START anchor, the END anchor, and every transition
  (their expected-side + key-path validations were already present — the deltas were the parses,
  the matches, and the row re-check; the start anchor was framed raw too, plan-review F1).
  15 legs per SDK at closeout (control + 8 tampers + the six pin/threading legs the
  delta- and diff-reviews forced; the five pin legs each isolated under their named
  mutation, the threading leg joint-by-construction per the settled diff-review record),
  each proven red-capable; TS 107 unit + conformance 283/283; Python 77 + conformance 283/283.
  mypy/ruff/purity/license clean. With this, all three shipped SDKs enforce the reference
  producer's full contract; the Go SDK (BAP-16) picks it up at authoring.

- **Rust SDK encode-path validation parity (BAP-15).** `encode_anchored_export` now enforces the
  Elixir reference producer's FULL validation contract (`anchored_export_codec.ex` encode):
  expected-side consistency (chain_id binding of both anchors + all transitions; the
  start/end sequence + hash bindings), a full `check_chain` re-check of the rows, gated parses
  + 7-field matches for BOTH anchors and every transition (the width/canonical gates now
  reached at encode), and the key-path walk (running key, strictly-after transition times,
  seen-list cycle guard, end anchor binding the final key with NON-STRICT `>=` chronology).
  19 red-capable battery legs at closeout (permissiveness 19 → 38), each mutation-proven; every leg
  verified against the Elixir reference oracle (20/20 fixtures — the 15 + control + the four
  closeout-lens additions; the receipt is a local re-runnable oracle artifact). Honest
  residuals, both NAMED: the TypeScript/Python SDKs carried the same producer permissiveness
  (CLOSED by the sibling entries above); the Rust SDK is a documented maximum-bounds posture
  at encode (the reference + siblings thread caller-tightened `expected.bounds` — carrying
  that is a public-API change, its own slice; the sibling identity fix also WIDENS TS/Python
  VERIFY-path acceptance for identity-override configurations, matching the reference; the
  Rust chunk-count parity fix is a latent verdict-tightening flip unreachable at the frozen
  maximum bounds — its pin is owed by the future caller-bounds slice).

- **Rust SDK conformance hardening (BAP-15).** The Rust verifier SDK now enforces the same two
  checks the Elixir reference has (closing the "separate check owed" note above, mirroring
  `18c6467`): (1) a decoded **signature-width gate** in all four `decode_*_parts` fns
  (`REQ1-BOUNDS-fixed-widths`, mirroring `runtime.ex:237`/`:259`,
  `boundary_anchor_codec.ex:88`, `key_transition_codec.ex:120`) — public verdict flips on
  `decode_grant`/`decode_proof` (accepted a wrong-width signature at decode) and
  `encode_anchored_export` (the start-anchor parse never width-checked its signature segment);
  (2) **canonical-form equality** for boundary-anchor + key-transition compacts — the four
  validators now assert `jcs_encode(value) == segment_bytes` for the protected header AND
  payload (mirroring `boundary_anchor_codec.ex:95-96,118-119` +
  `key_transition_codec.ex:127-128,151-152`), so a non-canonical (member-reordered) segment is
  rejected across `assemble_compact`, `verify_historical_anchor`, `verify_key_transition`,
  `encode_anchored_export`, and `verify_anchored_export`. Nine red-capable battery legs
  (4 canonical + 2 decode-width + 1 export-encode width + 2 canonical-exclusion pins + the
  four closeout-lens legs), each mutation-proven. Verified: cargo 338 unit + conformance 283/283 + permissiveness 38;
  clippy/fmt clean. Honest residuals, all
  routed: the encode path validates less than the reference producer — it frames the END
  anchor AND every TRANSITION raw without parsing them (the reference parses both anchors +
  all transitions through the width/canonical-gating codecs,
  `anchored_export_codec.ex:40-52`), binds the start anchor by sequence only (the reference
  matches all signed fields via `anchor_matches?`/`transition_matches?`), and does not
  re-check rows against the chain at encode (`ConsumptionChain.check`). A non-canonical or
  wrong-width end anchor or transition is therefore still accepted at Rust encode (probe-
  proven; caught downstream — `verify_anchored_export` gates all of them); closing these
  flips further verdict classes and is its own reviewed change. The Go SDK (BAP-16) picks
  both classes up at authoring.

- **BAP-17 — offline-eligible grant claims (reserve + specify).** Reserve the `ba_offline`
  grant-payload claim name in the [registries](docs/design/registries.md) (issuer-set offline
  floor limits: maximum value with explicit currency, maximum offline use count, offline-window
  expiry — a closed nested object; absence means online-only, per
  R-BAP-1). The offline floor-limit decision record
  carries the activating-major mechanism to spec quality: the closed `{cnt, cur, max, win}` object,
  the non-authorizing facts contract (an `offline_eligible` flag + `win` only — magnitudes are read
  from the decoded grant, not redacted facts), malformed⇒`:invalid` (online-only is the *absent*
  default), a wire-layer `max × cnt` ceiling (the cross-language SDKs compute exposure in fixed-width
  integers), the `ba_dlg` attenuation composition, and the freshness scoping. **Activation is a
  successor contract-major** — the closed v1 profile rejects `ba_offline` today (the `v0.1.0`-amend
  alternative was considered and is the heavier path: the published governance change-class rule, the
  three corpus-SHA-pinning SDKs, and intra-major fragmentation). This is a design-only slice: zero
  `lib/`/`docs/protocol-v1.md`/`priv/conformance/` wire-behavior change (mirror BAP-11/BAP-14); an
  R-BAP-2 legacy-rejection tripwire is added under `test/`. The offline runtime arc (private
  `bounded_authority` BA-20..23) is successor-major-gated by this reservation.

- Close the **BAP-15 Rust verifier SDK** (Tasks 15–17 + closeout; the library Tasks 1–14 were already
  landed and verified green). `sdks/rust/tests/permissiveness.rs` is the named per-language permissiveness
  battery exercising all six verifier-SDK decision record closures through the public crate boundary (duplicate-reject,
  source-order preservation, raw-lexeme ceiling, single-value, int/float tag distinction, base64url
  pad-bits), each documented with its red-capable mutation. Closure #6 — the `(d)`-class per-node encode
  bounds — is now enforced IN `jcs_encode`'s recursion (depth + total_nodes + a per-node `jcs_bytes`
  early bail), so a hand-built value passed directly to the public primitive cannot force unbounded
  recursion, traversal, or intermediate allocation. This narrows `jcs_encode`'s accept set
  (verdict-preserving on every corpus case — all corpus inputs are decode-bounded within the same
  ceilings; verified lib 331/331, conformance 283/283); the depth and total_nodes guards are proven
  red-capable by live mutation. `sdks/rust/tools/purity_check.sh` + `license_check.sh` enforce the
  lib-path purity invariant (no I/O/clock/RNG/network/env in `src/`; `#![forbid(unsafe_code)]` is the
  compile-time `unsafe` half) and the runtime dependency-license allowlist (15 runtime deps, all
  permissive; dev-deps excluded as non-consumer-facing) — both shellcheck-clean and red-capable. The
  `rust-conformance` CI job ([`.github/workflows/sdks.yml`](https://github.com/baselabs/bounded_authority_protocol/blob/main/.github/workflows/sdks.yml)) runs fmt + clippy + the full `cargo test`
  (unit + permissiveness + 283-vector conformance + census) + purity + license on the MSRV 1.81
  toolchain. The publish guard now scans `sdks/*/Cargo.toml` and blocks `cargo publish` /
  `cargo release publish` / `cargo-release publish` / `crate-ci/cargo-release` (the SDK-graduation decision record). New docs:
  [`sdks/rust/README.md`](https://github.com/baselabs/bounded_authority_protocol/blob/main/sdks/rust/README.md) and the deployment guide
  (`docs/deployment/rust-sdk.md`) (AWS Lambda `provided.al2023`; PostgreSQL
  `plrust` — ed25519-dalek-based verification is NOT plrust-trusted-mode-compatible as built). The three
  signed anchored-export rows in [`docs/design/conformance-contract.md`](docs/design/conformance-contract.md)
  are updated to their landed case ids. Zero wire byte, bound, or verdict change to the Elixir package.
- Close the BAP-15 prerequisite spec/corpus gaps (no-key half). `docs/protocol-v1.md` gains a
  normative **JCS string and number serialization** subsection transcribing RFC 8785 §3.2.2.2–3
  (control-range escapes, raw DEL `U+007F`, ECMAScript `Number::toString` float thresholds
  `e < -6` / `e >= 21`, `-0`→`0`, shortest-round-trip digits — both ECMA §7.1.12.1 and TC39
  §6.1.6.1.20 cited). The conformance corpus grows **259 → 280 cases**: 5 JCS float cases pinning
  both threshold sides, a raw-DEL bare-string case, an astral-codepoint (U+10000) raw-emit case, a
  float `cast_arguments` request-digest case, malformed-UTF-8-member-name and float-magnitude
  `json.decode` rejects, three malformed-IPv6 `uri.normalize` rejects, a key-locator
  protected-only (empty-segment) valid case, three `check_chain` rejects (canonical re-encode,
  sequence-zero, genesis previous-hash forge), the byte-level cross-vendor findings for
  `encode_consumption_entry` (seq-1 nonzero previous), `boundary_anchor_signing_input` (seq-0
  nonzero chain_hash), `encode_anchored_export` (start-anchor sequence binding — a signed start
  anchor carries a bogus sequence, isolating `start.sequence == first_sequence-1`), and
  `verify_anchored_export` (empty chunk). Every invalid case is a one-defect **skip-would-accept**
  construction (a verifier that drops its target check would accept it) — proven by defect-injecting
  each target check in the independent runner and confirming the case flips. The two
  `encode_/verify_anchored_export.maximum_plus_one` `n_a` reasons are corrected to name all three
  bounds (`archive_bytes` + `archive_chunks` + `historical_key_transitions`) and the loader
  representation constraint. The independent Node runner is strengthened to mirror the reference's
  IPv6-structure (node:net `isIP`, replacing a crude charset check), consumption-entry and
  boundary-anchor genesis bindings, `check_chain` positive-`first_sequence` and canonical-rehash
  (hashing the canonical re-encode, not raw row bytes, matching `ConsumptionChain.parse_row/1`),
  anchored-export start/end anchor bindings, and verify per-chunk emptiness — the invariants the new
  cases exposed; every new case is dual-verified (`agreed=280 disagreed=0`). SDK
  `CERTIFIED_INDEX_SHA` constants (Python hex + TypeScript base64url) rebind to the new `index.json`.
  The encode_anchored_export binding case's authoring key is seed-generated and never enters the
  import-boundary census (encode frames signatures, never verifies them), so
  `public_key_fingerprints` stays 8 and the manifest three-partition is unchanged.
- Close the BAP-15 prerequisite corpus gap (signed half). The conformance corpus grows
  **280 → 283 cases** with three signed `verify_anchored_export` cases exercising the chain-and-archive decision record's
  authenticated key-transition path: `#2` non-monotone chronology (two transitions with non-monotone
  `effective_at` → `invalid_time`), `#2` fingerprint cycle (B→C→B → `invalid_key`), and `#3`
  one-key/zero-transition valid (the equal start/end-time same-key case). Three new seeded Ed25519
  keys join `public_key_fingerprints` (**8→11**) since the export verifier imports them; their seeds
  never enter the corpus. The independent Node runner is strengthened to mirror the reference's
  cross-transition invariants (`validate_expected_key_path`): strictly-increasing transition times,
  fingerprint no-cycle, and the end-anchor chronologically at-or-after the last transition — gates
  the per-element compact checks cannot express. Every new invalid case is a one-defect
  **skip-would-accept** construction (a monotone / no-cycle variant accepts); every new case is
  dual-verified (`agreed=283 disagreed=0`). SDK `CERTIFIED_INDEX_SHA` constants (Python hex +
  TypeScript base64url) rebind to the new `index.json`; the vector manifest's canonical fingerprint
  set grows 19→22 and the corpus partition 8→11.
- Record the SDK graduation and publish-topology decision: cross-language verifier SDKs are
  authored under `sdks/` (per the verifier-SDK decision record) but each
  graduates to its own per-SDK repository (`bounded_authority_protocol_<lang>`) on first
  publication; the decision boundary is publication irreversibility, not SDK count
  (the SDK-graduation decision record). Zero wire byte, bound, or
  verdict change.
- Ship the SDK-graduation decision record enforcement layer: a tracked pre-commit hook
  (`scripts/hooks/pre-commit`, single pattern source `scripts/check_sdk_publish_infra.sh`, installed
  via `scripts/install-hooks.sh`) and the `sdk-publish-guard` CI job
  ([`.github/workflows/sdk-publish-guard.yml`](https://github.com/baselabs/bounded_authority_protocol/blob/main/.github/workflows/sdk-publish-guard.yml)) that together
  reject SDK registry-publish infrastructure committed to the monorepo. Honesty limit: catches
  committed publish infrastructure, not a literal ad-hoc publish run against a working tree; CI on
  main is the non-bypassable backstop, the local hook is honor-system. Bypass:
  `git commit --no-verify`.
- Add ROADMAP rows BAP-15 (Rust verifier SDK) and BAP-16 (Go verifier SDK) — typed reimplementations
  of the frozen v1 profile from spec + corpus alone, authored under `sdks/` and graduating per
  the SDK-graduation decision record. Post-1.0 rows; no code shipped.
- Ship cross-language verifier SDKs (TypeScript `@bounded-authority/verifier` + Python
  `bounded-authority-verifier`) under `sdks/` — typed reimplementations of the frozen v1 profile from
  the spec + corpus alone, with no code-level derivation from the Elixir reference
  (the verifier-SDK decision record). Each passes all 283 conformance vectors
  (recomputed from scratch), asserts the corpus `index.json` SHA at startup, and proves every
  permissiveness closure red-capable via a per-language mutation-gate. They are verifiers, not
  authority runtimes (no key selection, replay reservation, or execution grant). NOT in the Hex
  package `files:` (they are not Elixir). The `sdks-conformance` CI job
  ([`.github/workflows/sdks.yml`](https://github.com/baselabs/bounded_authority_protocol/blob/main/.github/workflows/sdks.yml)) gates every `sdks/**` /
  `priv/conformance/**` change. Zero wire byte, bound, or verdict change to the Elixir package.
- Draft the capability-authorization extension as a pre-submission package for the MCP
  experimental-extension track (`docs/extensions/` — a draft `.mdx`, a draft
  Extensions-Track SEP, and an Agent Payments Protocol (AP2) mandate-mapping note;
  the capability-authorization extension decision record), documenting the already-normative
  v1 protocol. Identifier `io.bounded-authority/capability-authorization`. Partial conformance to the
  MCP official-submission bar (SEP-2133); official submission is gated on external preconditions
  (a reference implementation in an official MCP SDK, a working group + Extension Maintainers +
  sponsor, SEP acceptance, and
  IANA registration of the `ba_*`/`ba+*` names via BAP-12). The extension documents are repo-tracked,
  excluded from the Hex package census (pre-submission drafts). Zero wire byte, bound, or verdict
  change.
- Publish the governance policy (change classes, change-control triggers, errata no-verdict-flip
  prohibition, deprecation windows, security-release handling) as a standalone normative project
  document ([docs/governance.md](docs/governance.md); the published-governance decision record)
  — a companion republication of the charter § Governance, which remains the authoritative source.
  [SECURITY.md](SECURITY.md) cross-references the verdict-change rule; the
  [errata registry](docs/errata.md) header retargets to governance.md as the published policy home;
  `SECURITY.md`, `docs/governance.md`, and `docs/design/standards-track.md` join
  the local-harness critical-surfaces manifest (SECURITY.md a retroactive gap close since BAP-06). Zero wire byte,
  bound, or verdict change.
- Resolve a contradiction in the deprecation/security policy: the twelve-month deprecation-window
  minimum (`REQ1-EVO-deprecation-window-minimum`) is scoped to planned deprecations, and a security
  contract-major (one remediating a verdict-changing vulnerability) is exempt — its accelerated
  overlap window is published at announcement, proportional to severity, and a deployment-decided
  sunset, never a silent change (the security-release decision record;
  charter § The evolution contract + § Governance; [governance.md](docs/governance.md) re-synced).
  Deployment-policy refinement only — zero wire byte, bound, or verdict change.

### Changed

- **Documentation corrections (2026-08-18 alignment-audit items 1, 4, 7).** SECURITY.md's "most
  recent package-bearing verified source" re-anchored from the BAP-06 head to the SDK
  behavioral-closure cluster head `c281938`, with the superlative date-bounded ("as of
  2026-08-18") so the claim expires visibly instead of silently: CI run 32118915019 and
  supply-chain run 32118915034 passed at that exact revision, and the doc's own verification
  recipe was re-run against the downloaded archive — checksum OK (ubuntu-built archive SHA-256
  `c9b5b0cf…`), SLSA build-provenance attestation verified constrained to this repository, the
  supply-chain workflow, `refs/heads/main`, that source digest, and GitHub-hosted runners; BAP-06
  `4c64be3` joins the earlier-heads list. The TypeScript conformance runner's startup comment
  miscited the corpus binding as the verifier-SDK record's enforcement-posture decision — corrected to its corpus-binding decision, matching
  the file's own correct citations. usage-rules rule 13's era-frozen "BAP-04 verification is
  implemented" now states the v1 verification surface is implemented. Docs-only — no code, wire,
  bound, or verdict change.
- **Documentation reconciliation — status prose frozen at the BAP-10 era (alignment-audit items
  1–9).** `AGENTS.md` "Current state" rewritten from the tracker: names the closed set (BAP-00..06,
  08..11, 13..15, 17), the three verifier SDKs under `sdks/` (verifier-SDK and SDK-graduation topology,
  none published), the decision records, and the 283-case corpus + verifier CLI. README's status section
  corrected the same way; the BAP-07 framing in README, SECURITY.md, and the release-candidate
  contract now records the maintainer decision (Hex publication deferred — internal consumption via
  the `v0.1.0` git tag at `c65d3be`, not a registry pin). SECURITY.md now cites the most recent
  package-bearing verified head (BAP-06 `4c64be3`) with the BAP-05/BAP-04 history retained. The
  TypeScript and Python SDK READMEs no longer point Install at registry URLs that do not resolve —
  they carry the Rust README's reserved-identifier/not-yet-published framing, and "one of two" SDK
  prose corrected to three; `sdks/README.md` floor corrected to Node >= 22 with the not-published
  disclaimer. `docs/design/offline-authorization-requirements.md` status now points at the closed
  BAP-17 row (the offline floor-limit decision record). ROADMAP repairs: BAP-17 added to the complete list, the Rust façade count
  corrected 15 → 17 (the exported public contract), and amendment #2's garbled closure/residual
  splice repaired so the `assemble_compact` maximum-bounds residual stands as its own sentence,
  consistent with amendment #3's restatement. Docs-only — no code, wire, bound, or verdict change.

## 0.1.0 release-candidate record — 2026-08-17 (published above as [0.1.0])

### Added

- Initialize the public Apache-2.0 repository and cold-start architecture authority.
- Define the deterministic verifier boundary, public/private dependency direction, protocol
  charter, threat model, conformance contract, decision record, and implementation roadmap.
- Close BAP-00 after public-remote verification, cross-repository documentation reconciliation,
  security-contract hardening, local-link/browser checks, independent reviews, and tamper-gate
  verification.
- Add the unpublished `:bounded_authority_protocol` 0.1.0 Mix package with zero production
  dependencies, no application callback, and no supervision tree.
- Enforce the pure-library boundary across source AST, compiled imports, application metadata,
  dependency declarations, and the exact packed/unpacked Hex archive.
- Add full local quality, coverage, documentation, advisory, closed license, CycloneDX,
  fresh-consumer, public CI, checksum, provenance-attestation, and SBOM-attestation gates.
- Freeze the normative v1 header, claim, selector, JSON, encoding, URI, domain-separator, bound,
  and fixed-error tables with primary RFC and OTP provenance.
- Add bounded ordered JSON decoding with recursive duplicate rejection, strict canonical
  base64url decoding, and a protected-header-only untrusted key locator.
- Add allow, deny, malformed, exact-boundary, deterministic malformed-input sweep, symmetric
  numeric-magnitude, package-consumer, and purity-boundary proof.
- Enforce raw numeric-lexeme bytes and exact decimal magnitude before OTP conversion; validate the
  companion Draft 2020-12 schemas with an independent validator and distinguish their structural
  code-point limits from the normative UTF-8 byte contract.
- Align the tracked roadmap with its authored-row contract while preserving BAP-00 through
  BAP-07 identities and exact dependency labels.
- Document the exact public `BoundedAuthorityProtocol.V1.Json.decode/2` and
  `BoundedAuthorityProtocol.V1.Base64Url.decode/2` surfaces, tightening-only positive-integer
  limits, structural-schema boundary, and fixed value-free errors.
- Extend the packed external consumer to exercise both decoder success and rejection paths, and
  add mutation-red proof that escaped string content cannot hide a following over-limit number.
- Reconcile the BAP-02 final trusted-main receipts and degraded peer-review record.
- Add deterministic standard compact-JWS grant and RFC 9449 holder-proof producers, exact external
  signature assembly, and bounded grant/proof decoders.
- Add RFC 8785 canonical JSON, RFC 7638 public Ed25519 JWK thumbprints, bounded HTTPS URI
  normalization, type-preserving request digests, and conjunctive selector evaluation.
- Add standalone raw-grant verification and combined raw-envelope verification with exact
  issuer/audience/time/holder/request/nonce/operation bindings and redacted, non-authorizing
  verified facts.
- Add public-only grant/holder-proof vectors, independent Node verification, exact public-key
  census, meaningful byte-tamper and duplicate-member cases, portable timing/allocation bounds,
  architecture census updates, and unpacked external-consumer API proof.
- Close BAP-03 at package-bearing head `f322e08bba665374599b9f53c362966b6b59710a`
  after the supported CI matrix, complete quality/package boundary, independent Node verification,
  exact archive checksum, SLSA provenance, and CycloneDX SBOM attestation passed. The single final
  review admitted five findings; all five were fixed in one pass without review recursion.
- Add closed canonical consumption rows with domain-separated hashes and mandatory-boundary raw
  chain verification for genesis and continued ranges.
- Add deterministic standard-JWS boundary anchors and authenticated historical-key transitions,
  including derived RFC 7638 fingerprints and lower-inclusive/upper-exclusive validity windows.
- Add deterministic binary anchored-export framing and atomic raw-chunk verification of complete
  digest, exact out-of-band object version, exact EOF, ordered key rollover, both signed
  boundaries, and every canonical row.
- Add closed fixed-redacted non-authorizing chain, anchor, transition, and anchored-export facts;
  exact tightening-only archive bounds; constant-time fixed-width comparisons; and expanded
  source/BEAM architecture accounting.
- Add five Draft 2020-12 structural schemas and public-only same-key, rollover, shortened,
  relinked, same-ID/equal-time, signed cross-chain, signed reverse-time, and signed invalid-genesis
  evidence. The project-independent Node verifiers prove an exact two-way eleven-key census split
  into exact per-verifier sets observed at their public-key import boundaries,
  49 named decoded-byte, structural, boundary, coverage, chronology, and limit cases, two direct
  valid chain cases, and seven signed semantic-edge cases; the isolated mutation gate proves 47
  source-level invariants go red. Published fixture verdicts and complete redacted
  chain/anchor/transition/export facts are exact-checked rather than treated as commentary.
- Add the maximum-count, maximum-width worst-of-20 chain/archive resource gate, with every sample
  isolated in a fresh monitored process, plus the chain-and-archive decision record, normative documentation, and packed
  external-consumer coverage.
- Validate every nested expected field and historical-key shape before archive hashing or parsing;
  reject duplicate outer conformance JSON members; and detect self-identifying Ed25519 PKCS#8 DER
  regardless of its surrounding field name.
- Make Ed25519 public-key/signature and SHA-256 digest widths immutable bounds so tightening cannot
  produce or admit a structurally nonconforming cryptographic value.
- Centralize strict StringOrURI validation across grant/proof and BAP-04 identifiers so malformed
  percent escapes and non-URI bytes cannot enter any producer or verifier path.
- Preserve exact package-boundary accounting across supported Elixir compiler versions and pin
  bitstring match sizes required by Elixir 1.20.
- Close BAP-04 at package-bearing head `c4d7716de6499f29524e60638207b1c36e9484b3`
  after the supported CI matrix, complete quality/package boundary, independent Node verification,
  47/47 mutation battery, maximum-shape resource gate, exact archive checksum, SLSA provenance,
  and CycloneDX SBOM attestation passed.
- Author roadmap rows BAP-08 (capability-authorization extension proposal for the MCP
  `modelcontextprotocol/ext-auth` extensions repository plus an AP2 mandate-mapping note;
  depends on BAP-04 only) and BAP-09 (thin TypeScript and Python verifier SDKs consuming only
  the published spec and vectors; depends on BAP-05). Each row's own decision record lands when its work
  starts; neither changes any wire format, limit, or verification rule.
- Close the check_envelope selector-binding gap: add a non-trivial (`equals`) selector valid case
  plus an `invalid_selector` case, teach the independent Node verifier to evaluate grant selectors,
  and add a `selector-reject` mutation — so a verifier that ignores grant selectors now fails the
  corpus. Uses two new deterministic conformance keypairs (census 6→8); a companion
  `proof_signing_input` valid case carries the new holder key as a labeled field so the
  cross-verifier census discovery scan finds it (corpus 212→215).
- Close the check_envelope authority-binding gap the same re-signing capability exposed: the
  holder (`cnf.jkt`), grant (`ath`), request-argument (`ba_req`), and operation (`ba_op`) bindings
  had no corpus case isolating them, so a verifier omitting any one of them still scored a perfect
  corpus run — omitting the holder binding accepts any holder's proof against any grant. Add four
  `invalid_claim` cases (each a one-defect variant of a shared valid base, with the named binding
  as the sole rejecter) and four matching mutation-battery entries. The `ba_op` case needs a
  hand-built proof payload: the request digest is computed over the server-derived operation, never
  over the proof's own `ba_op` claim, so only a dishonest producer — which the façade cannot be —
  emits a proof whose two operation fields disagree (corpus 215→219).
- Add an empty-path `invalid_selector` case so the independent runner's selector shape and width
  validation is falsifiable: the official rejects an empty selector path at grant decode, and a
  matcher treating `[]` as "the root" would accept what the official refuses (corpus 219→220).
- Close the independent runner's remaining permissiveness against the official decoder, so it can
  no longer certify a grant the reference implementation refuses. Six divergences, each now carried
  by a corpus case and a mutation entry: an extra member inside the closed `cnf` map; a
  non-printable byte in an operation name (the official requires printable ASCII); a structurally
  invalid selector on a NON-matching operation, and duplicate operation names (the official
  validates every operation and enforces global name uniqueness, not just the requested one); a
  lone surrogate in a selector path (the official requires valid UTF-8); and a `__proto__` member
  in a selector value, which the tagged projection silently dropped to the prototype setter so that
  two structurally different values compared equal — a collapse that reached the request digest as
  well as selector matching. Selector values are now also held to the protocol JSON bounds. The
  same operation validation is applied on `verify_grant`, which reaches the same official decode
  path (corpus 220→247; `check_envelope/invalid_encoding` 6, `verify_grant/invalid_encoding` 5).
- Add `.gitleaks.toml`: the `jwt` and `generic-api-key` rules are allowlisted for the conformance
  corpus and vector paths only, where all 283 findings are JWT-shaped high-entropy public test
  material (109 `jwt`, 174 `generic-api-key`, the latter entirely key fingerprints). Every other
  default rule still applies in those trees — a `ghp_…` token committed there is still caught —
  and every rule applies everywhere else. Stated residual: a credential matching ONLY those two
  rules, under those two machine-generated fixture directories, is not flagged.
- Complete the payload-field decode mirror, closing the last of the runner's permissiveness
  against the official decoder. The runner previously validated only the grant/proof fields the
  expected-context comparison happened to touch; it now mirrors decode_grant_fields /
  decode_proof_fields field for field — issuer/grant-id/audience StringOrURI validity and length,
  audience count bound and uniqueness, coherent times (iat<exp, nbf<exp), method token charset,
  invocation UUID shape, and htu normalization — wherever it reads a grant or proof payload
  (check_envelope, verify_grant, decode_grant, decode_proof). Nine cases on the DECODE surfaces
  (no expected context to mask the validator, so each is the sole reject reason) with six mutation
  entries prove them; corpus 236→247. One bound stays out of scope by construction: the aggregate
  total_nodes/depth budget the official applies across the whole payload cannot appear inline (such
  an input exceeds string_bytes) and these surfaces take no `.raw` sidecar, so it is exercised at
  the json.decode surface instead. Two further field checks the first mirror pass missed,
  found by the closeout review: the StringOrURI structural gate (the official validates
  iss/jti/aud through URI.new, so it rejects a non-numeric port, an unterminated IPv6
  literal, or a double `@`; the byte-only mirror accepted them — now matched to URI.new
  across a 56-input boundary set, using node:net for IPv6 literals so the mirror is neither
  looser nor stricter), and the optional proof nonce (present must be a 1..nonce_bytes
  well-formed string). Each with a decode case and mutation entry; corpus 236→247.
- Close three further runner/official divergences the final cross-vendor pass found in selector
  value validation, each now carried by a corpus case and a mutation entry: the magnitude bound
  (the official caps |value| at 9007199254740991 and rejects 2^53; the runner checked only
  finiteness), and a one-byte floor on object member keys that the official does not impose — the
  runner rejected `{"":1}`, which `Json.decode` and `Jcs.encode` both accept, making it STRICTER
  than the reference. Corpus 233→236 (a valid empty-object-key case pins the strictness fix).
- Adopt the standards track charter (the standards-track decision record, `docs/design/standards-track.md`,
  `docs/design/registries.md`, `docs/errata.md`): evolution above the permanently closed wire
  format via parallel contract-majors with published deprecation windows; the current profile
  named as cryptographic suite `BAP1-Ed25519-SHA256` with an ML-DSA succession path and
  cross-suite countersignature design for long-retention evidence; RFC 2119 requirement
  identifiers with corpus traceability and IANA registration templates as release-gating roadmap
  rows; delegation-with-attenuation decided (chained grants, `ba_dlg`/`ba_obo`/`ba+cap-delegated`
  reserved, the conjunctive selector algebra as the attenuation language, no caveat DSL);
  revocation and principal-binding deployment guidance homed; governance (change classes, errata
  registry with the no-verdict-flip invariant, comment-window triggers) published. No wire byte,
  bound, or verdict changes; new roadmap rows gate first publication on the unretrofittable items.
- Add the portable v1 conformance corpus (247 cases across 28 surfaces with a total
  surface × class applicability matrix, `.raw` sidecars for oversize wire inputs) and the pure
  `Conformance.Corpus`/`Runner`/`Report` core that loads, executes, and reports agreement.
- Harden the corpus against vacuous green: author the invalid vectors the crypto verifying
  surfaces were missing — algorithm-confusion (`alg:"none"`), meaningful-byte signature/commitment/
  anchor tampers (via a target-addressed tamper loader that binds a single-byte flip to the
  compact / grant / proof / row / chunk bytes), and archive/chain/envelope binding failures — plus
  exact-bound and maximum-plus-one pairs for every `json.decode` structural limit. Each new invalid
  vector is a one-defect construction confirmed rejected by BOTH the official facade and the
  independent Node runner; every remaining not-applicable applicability cell carries a falsifiable
  inexpressibility reason. The independent Node runner gained the request-binding (method / URI /
  invocation / operation / nonce) and object-name-byte checks the new vectors surfaced.
- Add the deterministic offline verifier CLI (escript `bounded_authority_conformance`,
  `--corpus DIR` required, exits 0/1/2) with an exact-path purity carve-out (File/IO in `cli.ex`,
  `System.halt` in `cli/main.ex` only) enforced by the architecture gate.
- Add the independent Node second-implementation runner
  (`conformance/corpus_independent.mjs`, node:* only) that recomputes every corpus verdict from
  scratch — making the corpus normative. Evolve the public-key census to three partitions
  (bap03 + chain_archive + corpus = the canonical set); the corpus self-census is hard two-way.
- Add stream_data property gates (JCS determinism/idempotence, base64url round-trip/pad rejection,
  URI normalization idempotence, facade closure totality) and a deterministic-PRNG fuzz gate.
- Add the source-isolated conformance mutation battery (proving the corpus integrity, CLI
  carve-out, and runner-verdict gates actually catch their named failures); wire both mutation
  batteries into `mix quality`. CI pins Node 20 for the quality job.
- Close BAP-05 with the corpus-format decision record (corpus formats, sidecar rule, published-artifacts definition,
  applicability matrix + n_a criterion, census evolution, CLI contract, carve-out shape,
  gate set). The corpus ships in the published package; the fresh-consumer check runs the packaged
  escript against the packaged corpus, proving published-set sufficiency.
- Close the remaining independent-runner permissiveness residuals surfaced by a cross-vendor design
  review (corpus grows 247 → 259 cases). The Node runner now mirrors the official on: the integer
  magnitude bound for every integer claim (a proof `iat` of 2^53 is rejected); the full
  `request_digest` gate on `cast_arguments` — operation validity, per-node bounds, `total_nodes`, and
  `jcs_bytes` on the type-tagged projection, not an unbounded digest; and whole-payload container
  depth on grant/proof payloads (a payload nested past depth 32 is rejected at parse). Fix the sibling
  `jsonDecode` per-node-type depth error (it rejected a 32-deep scalar-inner nest the official
  accepts — the too-strict direction that fails a conforming verifier). Twelve exact-bound /
  maximum-plus-one cases and seven source mutations pin each, each verdict confirmed against the
  official facade. The corpus-format decision record corrected: whole-payload depth and value-carried `cast_arguments`
  node/byte bounds are inline-expressible and now tested; only compact-carried whole-payload
  `total_nodes` and an inline 65-member `object_members` remain enforced-without-a-red-case.
- Close BAP-06: lock the 0.1.0 release-candidate public API surface (enumerated in the
  [release-candidate contract](https://github.com/baselabs/bounded_authority_protocol/blob/main/docs/release-candidate-contract.md) and enforced by the
  `@compiled_export_allowances` architecture-gate pin;
  the release-candidate decision record),
  add the `release.candidate` reproducibility gate (two cache-isolated builds, byte-equal SHA-256,
  wired into `mix quality`), and author the candidate-facing docs (release-candidate-contract.md,
  SECURITY.md, CHANGELOG `[0.1.0]`, README). Zero wire byte, bound, or verdict change. Published
  2026-08-20 — BAP-07 published the exact candidate after the connected gates passed.
