Skip to content

Releases: s0fractal/warrant

warrant 0.9.0 — the published package disagreed with the published pack

Choose a tag to compare

@s0fractal s0fractal released this 01 Aug 20:31

0.9.0 — the published package disagreed with the published pack

BREAKING for records with implausible timestamps; no real record is affected.

SPEC §2's integer domain is now ±(2^53−1) rather than int64, in all three
implementations, the JSON schema and the specification. Identity is SHA-256 over
RFC 8785 bytes and RFC 8785 §3.2.2.3 serializes numbers through an IEEE-754
double, so above 2^53−1 the canonical bytes stop being a function of the value:
ts = 9223372036854775807 canonicalizes to 9223372036854776000 in any
conforming JCS, and one logical record acquires two WarrantIDs. Reported by an
external Codex review. Wrapping was rejected — it maps two values onto one
WarrantID in a format whose identity is that hash, and breaks §6's
non-decreasing ts rule along prior edges.

Every JSON in this repository and its two siblings was scanned: no record, blob,
vector or fixture carried an integer outside the new range, so nothing migrates
and no WarrantID moves. The change was possible only while the user count is
zero.

Why this release exists at all, rather than waiting. Conformance pack 1.1.0
tests the new bound. warrant-verify 0.8.0 implements the old one. Measured
against the published wheel:

PERMISSIVE IMPLEMENTATION: 2 of 62 MUST-REJECT vectors were ACCEPTED.
  accepted: validate/reject-11-ts-=-2^53
  accepted: validate/reject-12-ts-=-int64-max

A stranger running pip install warrant-verify against the current pack would
have been told the reference implementation is permissive. The pack and the
package have to agree, and the package is the one that was behind.

Also in this release:

  • why no longer rejects records verify accepts. SPEC §5 says a junk
    co-signature MUST NOT invalidate a record that still carries a valid signature
    by body.actor.id; why required every signature to verify, handing that
    power to anyone who could write a file in the store. Both now use
    _well_signed, so there is one definition of "signed" rather than two.
  • §2's integer rule is enforced by an explicit walk over every integer rather
    than by min_sigs > len(actors) rejecting the same values coincidentally.
  • Conformance pack 1.1.0: 138 vectors, 62 MUST-REJECT, digest
    ddd825a8…. The corpus changed, so the version changed — 1.0.0 stays
    published unaltered, because two people holding one filename must not have
    two different files.

warrant 0.8.0 — the MCP server ships in the package

Choose a tag to compare

@s0fractal s0fractal released this 31 Jul 21:20

pip install warrant-verify now gives you the MCP server as a command:

pip install warrant-verify
claude mcp add warrant -- warrant-mcp-server --store .warrants

Until now the server lived in integrations/mcp-server/, outside the one root setuptools' flat layout allows, so it could not be in the wheel at all — the registry entry published yesterday offers a clone and nothing else. It is now impl/warrant_mcp_server.py with a warrant-mcp-server console script, and its CLI resolution stops pointing at a path that resolves to nothing once installed.

Two similarly-named programs, and the guard is executable rather than documentary. warrant-mcp is the sealing proxy — it wraps someone else's MCP server and seals the traffic. warrant-mcp-server is this server. Confusing them would be worse than shipping neither, so warrant-mcp refuses to start without a downstream command after --, warrant-mcp-server refuses one, and three release-gate cases assert that against the wheel's own parsers.

Verified as a stranger, not as an author. The wheel was built, installed into a clean virtualenv, registered with a real MCP host (✔ Connected, then removed), and all three tools were driven over stdio against the installed copy — including a decision whose ski@v1 reason re-executed (reproduced: true, atp_spent=18) and a one-byte tamper that correctly flipped the store to ok:false with a WarrantID mismatch.

A dropped plan item worth naming. The manifest was going to carry runtimeHint: "uvx". Measured against the built wheel, uvx --from <wheel> warrant-verify answers "An executable named warrant-verify is not provided by package warrant-verify" — a client composes runtime + identifier, and the identifier is the distribution, not the script. The schema has no field naming which console script to run, so the hint would have encoded a command that does not start. Omitted, as it is in 262 of the registry's 267 PyPI entries.

Also in this release: LISTINGS.md said this project was listed nowhere, hours after it was listed — corrected with the old sentence quoted rather than overwritten, and the registry's valid: true restated beside the measurement that a manifest naming a nonexistent package gets the same answer. And repo_map.py resolved the sibling repository only when the checkout directory was literally named warrant, so regenerating from a worktree silently rewrote every cross-repo citation to "resolves nowhere".

Not done here: the registry entry still points at a clone. Updating it needs this release on PyPI first — the registry 404s on a manifest version it cannot find, and proves package ownership by reading an mcp-name: marker out of the published README, which this release adds. No external party has implemented the conformance contract, and no independent gate has reviewed any of this.

warrant 0.7.1 — the runner stops telling beginners they failed

Choose a tag to compare

@s0fractal s0fractal released this 31 Jul 19:11

Two defects in the first thing a stranger sees, both in the conformance runner's report rather than its verdict.

A partial implementation was told it had failed. With zero wrong answers and 78 UNRUN — the state every implementer is in on day one — the report ended Not even base grade. See the failures above. There were none. It now says WITHHELD BECAUSE INCOMPLETE — nothing failed, confirms every answered vector was correct, and lists the three cheapest classes to write next.

--self-check accused itself of being broken. The README tells everyone to run it before trusting a green result; against a partial candidate it printed THIS RUNNER DID NOT NOTICE. The runner was fine — a mutation proxy has nothing to corrupt in answers that do not exist. INAPPLICABLE is now its own outcome, decided by running the candidate unmutated first and measuring what each mutation actually changed.

The risk in that fix was making the negative control quietly tolerant, which would be worse than a confusing message. It is asserted red in three directions: a mutation that applies and is missed still fails; a mutation that corrupts nothing about a class the candidate does answer reports THE NEGATIVE CONTROL IS BROKEN; a run where nothing applies is INCONCLUSIVE and says it establishes nothing. The old scoring also credited one mutation with any UNRUN row including the candidate's own — a false DETECTED, now gone.

The worst-hit case was CONTRACT.md's own worked minimum: the program the document tells a newcomer to start from declines everything, so the crash mutation turned 129 UNRUN into 129 ERROR and was still scored a miss. The regression test now extracts that program from CONTRACT.md so the example cannot rot.

The conformance tarball is re-cut. Changing run.py moves the pack's bytes and the digest SPEC §8.6 pins; leaving the 0.7.0 asset in place would have had it compare against a line that no longer matches, which reads as tampering on the one artifact whose purpose is checkable integrity. Same pack version 1.0.0, corrected bytes — the contract did not change, only the runner's diagnosis of it.

Also: repo_map.py resolved the sibling repository only when the checkout directory was literally named warrant, so regenerating from a worktree silently rewrote every cross-repo citation to "resolves nowhere". Identity now comes from the git remote.

No independent gate ran; nothing adopted. No external party has implemented the contract.

warrant 0.7.0 — conformance a stranger can run

Choose a tag to compare

@s0fractal s0fractal released this 31 Jul 04:20

A third party can now check their own Warrant implementation without cloning this repository and without running ours.

curl -LO https://github.com/s0fractal/warrant/releases/latest/download/warrant-conformance-1.0.0.tar.gz
tar xzf warrant-conformance-1.0.0.tar.gz
cd warrant-conformance-1.0.0
python3 run.py --candidate "./my-verifier probe"

32 KB, Python standard library only, no network after the download. Measured: 0.6 s to download and extract, ~7 s for a full settlement run against a Python candidate, ~35 s for --self-check. (An earlier version of these notes said "under a second from tar to verdict" — that measured extraction against a trivial candidate and was the wrong number for a real first run. The seconds are your program's start-up, paid once per vector, because the runner spawns the candidate rather than holding a session open.) CONTRACT.md is the whole thing your implementation has to satisfy: read one JSON object from stdin, print one to stdout, exit 0. About a page, with a nine-line worked example.

Why this release exists. The pack asks you to validate the runner against a known-good implementation before trusting a green result — and until now that was impossible from outside, because the probe surface all three implementations gained landed after 0.6.0. pip install warrant-verify gave you a binary the pack could not talk to. It can now:

pip install warrant-verify
python3 run.py --candidate "warrant probe" --claim settlement

What the runner refuses to let you get away with

The expected value is never sent to your program, so nothing can be echoed back — a check that supplies the input it is testing measures nothing.

Exit 0 means you produced an answer, including "no". The verdict travels in the body. A nonzero exit conflates I reject this with I crashed, so it scores ERROR. Declining is explicit (unsupported); silence is not a skip.

Grades are claimed and tested, not assumed. Claim base and you are tested on base. Claim settlement while declining classes and the grade is withheld with exit 2 — impl-rs claims base and is told so, rather than being quietly marked incomplete.

--self-check wraps your program in a proxy that breaks specific answers on purpose and asserts the runner catches each one. Before you trust a green run, make the runner go red on demand. This repository has shipped gates that could not fail; that is why the mutation proxy is in the pack rather than in our test suite.

The negative half carries equal weight: an implementation that accepts everything passes every positive vector and is caught by PERMISSIVE IMPLEMENTATION: 58 of 58 MUST-REJECT vectors were ACCEPTED.

Also in this release

  • An approval boundary (integrations/approval/): wherever an agent action is sanctioned by an independent party — a graph interrupt, a tool-permission hook — the same four facts exist, so the boundary is built once and each binding is a page. A LangGraph 1.2.10 example and a permission-hook example ship with it; the study behind the decision, including why an OpenTelemetry bridge was declined, is in docs/integration-study.md.
  • An MCP registry manifest, deliberately without an install block: the server does not ship in this wheel, and a manifest claiming otherwise would be a lie in the first thing most readers see.
  • Every GitHub Action pinned to a commit SHA, with Dependabot now watching them, and the Lean toolchain installer pinned by commit and digest — it had been piped from a moving branch into sh inside the workflow that decides whether the proofs check out.

Verify this release

pipx install warrant-verify
curl -LO https://github.com/s0fractal/warrant/releases/latest/download/air-canada-pack.zip
unzip air-canada-pack.zip
warrant --store air-canada-pack/.warrants verify
warrant --store air-canada-pack/.warrants check b423b6a82c3451bfbd75563b39e6391093a64db57941d9247a61a6c620bd997f

The last line re-executes the reason on your machine and prints pass result=65cd957f… atp_spent=17.

What this release does not claim

tools/check.py is 38/38 with zero unrun and the three implementations agree byte-exact on all 133 conformance vectors. But no external party has implemented the contract, so "an afternoon in any language" is an estimate from writing the probe three times in languages we already knew. No independent gate has reviewed any of this — every reviewer was one model family, or an external model run by the same operator. Two such audits found real defects, credited where they were fixed. That is defect-hunting, not a gate, and the difference stays in the record.

warrant 0.6.0 — signatures name the protocol, and reasons are readable

Choose a tag to compare

@s0fractal s0fractal released this 31 Jul 00:30

Breaking. The signed message is no longer the bare WarrantID. SPEC v0.4 signs "warrant-sig-v1:" || WarrantID_raw — 47 bytes, still pure RFC 8032 Ed25519. A pre-0.6.0 signature MUST NOT verify, and nothing accepts both; warrant resign --key <keyfile> migrates a store you hold the key for, and no WarrantID changes because the envelope is not hashed. If you do not hold the key, the verifier says so by name instead of reporting a generic bad signature.

Why now: a key that signed any other protocol's raw SHA-256 digest signed a Warrant too. The cost of fixing that was at its global minimum with no external users and rises monotonically from here — so this release takes it rather than inheriting it.

Writing a re-runnable reason no longer means writing combinators

A check that any verifier re-executes offline was the point of this format, and authoring one meant hand-building SKI terms. WPL compiles a readable policy to exactly such a term:

fact days_until_travel: int = -12
fact fare_basis:        string = "bereavement"

check fare_basis == "bereavement" && days_until_travel >= 0

It borrows CEL's syntax so a Kubernetes or Envoy reader parses it on sight, and claims no CEL conformance — deliberately, because no CEL implementation is available offline and stdlib-only, and an unverified compatibility claim is a provenance defect in a repository about provenance. docs/authoring-checks.md takes an engineer who has never heard of a combinator from nothing to a working policy.

The compiler is not new trusted code. The verifier still re-runs the term; the compiler only produces it. Every compile round-trips its output through the Σ-GLYPH oracle against a plain-Python interpreter of the same source and refuses to emit on disagreement — and, after an external audit found --headroom emitting checks the verifier rejects (a negative value produced a wrong verdict, not an error), it now re-executes the serialized blob under its pinned ATP before writing. The artifact is validated, not the intention.

The Air Canada demo is unchanged where it counts: same check blob, same term, same atp_spent=17.

The normative surface moved into the spec

warrant.verify-report@v0 — the contract CI branches on — lived only in the README. It is in SPEC now, with the JCS escaping battery extracted from a Python script into machine-readable vectors, trust-config.json specified, JSON Schemas published for body/envelope/report/manifest, a draft application/warrant+json registration, and a registry policy for runtime and report tags. Plus THREAT-MODEL.md, consolidating what was scattered across five files, and a CHANGELOG.

A P0 fixed on the way: verify --settlement --trust-config reported 1 warnings in text and "warnings":0, "findings":[] in --json for the same store — Python's quiet path swallowed the unbound-signature finding that Go emitted in both. Two implementations disagreeing on a verification outcome is the severity this project reserves for exactly that.

Also

  • MCP server (integrations/mcp-server/): any MCP client files decision records with two lines of configuration.
  • Model actors are delegated, bounded actors under a root of authority held by a human — never the maintainer of record, never where non-repudiation rests. The retirement insight is kept, relocated under that framing. Custody is stated in counts rather than implied.
  • Every GitHub Action pinned to a full commit SHA; the jobs that mint an OIDC token no longer take a mutable ref.

Verify this release

pipx install warrant-verify
curl -LO https://github.com/s0fractal/warrant/releases/latest/download/air-canada-pack.zip
unzip air-canada-pack.zip
warrant --store air-canada-pack/.warrants verify
warrant --store air-canada-pack/.warrants check b423b6a82c3451bfbd75563b39e6391093a64db57941d9247a61a6c620bd997f

The last line re-executes the reason on your machine and prints pass result=65cd957f… atp_spent=17. You are not trusting a log; you are recomputing the argument.

What this release does not claim

tools/check.py is 33/33 with zero unrun, and the cross-repo seam against Σ-GLYPH is green. But no independent gate reviewed this work: every reviewer was either this model family or an external model run by the same operator. Two such audits did find real defects — a non-recursive file walk in the sibling's proof guard, and both P1s named above — and they are credited where they were fixed. That is defect-hunting, not a gate, and the difference is kept in the record rather than rounded away.

warrant 0.5.0

Choose a tag to compare

@s0fractal s0fractal released this 30 Jul 00:55

warrant 0.5.0

133 commits since v0.4.0. Two things define this release: a machine boundary a
CI system can consume, and — for the first time — release assets that make the
README's "no clone, no build, no account" quest actually true. Releases
v0.2.0–v0.4.0 carried zero assets, so the README's curl line 404'd; this one
ships the packs it promises.

The machine boundary: verify --store-mode --json

warrant verify --store-mode --json emits warrant.verify-report@v0 — a
closed schema (exactly seven top-level keys, exactly {level, subject, message}
per finding) with two guarantees a consumer may rely on: the errors/warnings
counts always bind the findings, and any future field ships under a new tag,
never inside @v0. Both implementations emit it; owner-side tests assert the
producer contract on every vector. This is the boundary the new GitHub Action
consumes.

Honesty note: 0.4.0 shipped while the README already documented this flag, so
pip install warrant-verify plus the README produced unrecognized arguments.
The fix is structural, not apologetic: tools/check_release_surface.py now
extracts every documented CLI invocation and validates it against the built
wheel, and the publish workflow fails if the artifact cannot do what the docs
say. That state is now unpublishable rather than merely regrettable.

Adoption surface

  • GitHub Action (s0fractal/warrant@master, or pin this tag): installs the
    verifier from PyPI, verifies a store or evidence pack, fails the job on any
    error, writes a summary, and exposes ok / records / errors / warnings
    / the full report as outputs. It does a capability check, not a version
    check — and a review found the version input was being spliced into shell;
    that injection is closed.
  • Release evidence packs: air-canada-pack.zip and cross-vendor-pack.zip
    (plus SHA256SUMS) are attached to this release. Built by
    tools/build_release_packs.sh, which refuses to ship anything key-shaped and
    verifies each zip the way a stranger will — unzipped, in an empty directory,
    with no repo on the path.
  • in-toto bridge: express a decision as an in-toto Statement v1, with a
    tamper matrix — the format stops being an island.

Third implementation, and verifier hardening

  • impl-rs now verifies stores, so three independent implementations
    (Python, Go, Rust) agree on what verifies and — checked against hostile
    stores — on what does not. The Rust Ed25519 is from scratch and was audited
    externally (two P0s found and fixed).
  • Verifier fixes found by external gates: a blob could be swapped at its own
    address and both verifiers said ok (the fix covers all five content kinds);
    lone surrogates are rejected in both implementations (one I-JSON domain);
    policy/ski/json-blob paths no longer crash on hostile input; why's exit
    status now matches its honest output; a supplied keyring is no longer
    reported as absent. The BOM refusal is now named in the canon rather than
    incidental.

Settlement: a rule that can say when an argument is over

tools/settle.py under policies/gate-settlement.json: a gated item is
settled unless a reproduction executes — silence blocks nothing, and
blocking power belongs to re-runnable evidence, not to a signature. Built after
eight consecutive single-family review gates produced eight AMENDs and no
convergence, while a three-family gate found the P0s immediately. The rule is
itself gated (47 cases plus a randomized fuzzer), and its own gates found and
fixed defects in it — including a sort order that silently swallowed P0s.

Governance and record

  • AGENTS.md: no direct-to-master, no self-declared roster authority — written
    after an agent did both.
  • SECURITY.md and CITATION.cff: a project about provenance had no
    disclosure process; now it does.
  • Prior-art record attested by parties that are not us; maintainer delegation
    written down as a bounded lease.
  • EU AI Act Article 12 mapping (DRAFT, dated because it decays).
  • WRT-002 key-state / lifecycle-authority design continues as a draft
    eight revisions in, still design only, not adopted.
  • tools/check.py: one command that runs every claim in the repository —
    currently 27 checks — and reports what it could not run.

Verify this release

pipx install warrant-verify
curl -LO https://github.com/s0fractal/warrant/releases/latest/download/air-canada-pack.zip
unzip air-canada-pack.zip
warrant --store air-canada-pack/.warrants verify
warrant --store air-canada-pack/.warrants check b423b6a82c3451bfbd75563b39e6391093a64db57941d9247a61a6c620bd997f

The last line re-executes the reason on your machine and prints
pass result=65cd957fee7e… atp_spent=17. You are not trusting a log; you are
recomputing the argument.

warrant-verify v0.4.0

Choose a tag to compare

@s0fractal s0fractal released this 16 Jul 23:16
2de1d17

Adds ski_policy (re-executable boolean policy predicates) and warrant-anchor (RFC 6962 Merkle batching), plus the warrant-mcp and warrant-anchor console commands. Drops an accidentally committed Go build cache.

warrant-verify v0.3.0

Choose a tag to compare

@s0fractal s0fractal released this 16 Jul 22:42
952ff03

What's Changed

  • Package warrant as an installable offline verifier + evidence-pack v0 + MCP sealing by @s0fractal in #8

Full Changelog: v0.3...v0.3.0

v0.3 — Settlement at scale

Choose a tag to compare

@s0fractal s0fractal released this 07 Jul 19:02

Settlement semantics, multi-root stores, and key state — specified and implemented in both Python and Go, differential-tested.

The arc behind this release: a model's governance critique of its own trust layer (issues #5#7) → a proposal torn down and rebuilt through a three-family adversarial gate (Codex broke rev 1, Gemini broke rev 2 including part of Codex's fix, DeepSeek refuted rev 3's hardest attack and converged) → SPEC v0.3 → implementation by a fourth agent in an isolated clone → maintainer cold verification, including a spot-check that pinned the one test case the implementation run skipped.

Highlights:

  • Re-litigation is executable: re-opening a settled subject needs new evidence or a new outcome fingerprint — prose never re-opens anything. Novelty is format; relevance is policy.
  • Multi-root stores: well-signed ≠ settlement-active; adoption is policy-authorized and jurisdiction-scoped; genesis.json is advisory, never a trust anchor.
  • Key state from warrants: rotation = proof-of-possession + quorum of already-bound keys; DAG order, never wall-clock; conflicted keys are excluded from quorum so a compromised key cannot veto its own replacement.
  • Zero body schema changes — every v0.1/v0.2 record verifies byte-identically; v0.3 activates via policy blobs and verifier configuration.
  • tests/settlement.py: 13 scenarios where both implementations must produce identical outcomes and warning strings.

Every decision in this arc is a signed warrant in .warrants/ (8 records): python3 impl/warrant.py why 9f9d35a5… walks the whole chain.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EMGpJLpAenjdvTeR5r7w4D

v0.2.0 — ski@v1

Choose a tag to compare

@s0fractal s0fractal released this 05 Jul 18:08

The reserved runtime lands: a check is a content-addressed SKI term evaluated per Σ-GLYPH Book I v0.5; the verdict is a hash comparison; work and peak memory are bounded by the ATP budget — re-verifying a stranger's reason is safe by construction. Worked vectors in examples/ski/ (TV-10 as a warrant, 20 ATP, zero leaf blobs thanks to genesis intrinsics). v0.1 bodies still reject ski@v1; that rejection stays conformant. warrant check <hash> re-runs one.