Skip to content

Protocol v1.1.0 + v1.2.0; lift-plugin protocol; 5-language peer impls#5

Merged
TSavo merged 67 commits into
mainfrom
feat/protocol-v1-contract
May 1, 2026
Merged

Protocol v1.1.0 + v1.2.0; lift-plugin protocol; 5-language peer impls#5
TSavo merged 67 commits into
mainfrom
feat/protocol-v1-contract

Conversation

@TSavo
Copy link
Copy Markdown
Owner

@TSavo TSavo commented May 1, 2026

Summary

Two signed protocol cuts (v1.1.0, v1.2.0), the lift-plugin protocol with three working CLI plugins, five peer-language implementations, and the LINQ-lifter existence proof. 61 commits.

Protocol cuts

  • v1.1.0 (signed, frozen): scorched-earth cut introducing contract memento (pre/post/inv/outBinding), implication memento (signed Z3 witness), three-tier handshake algorithm, maximal-uniformity IR (flat quantifiers, unified operands, atomic uses name, no sort on var/ctor), self-identifying cryptographic primitives (blake3-512: / ed25519: everywhere). Catalog CID blake3-512:5b770182...19f05cd4. Signed under foundation v0 (test seed).
  • v1.2.0 (signed, additive over v1.1.0): four pluggability protocols — agent-plugin-protocol, ir-compiler-protocol, multi-solver-protocol, lift-plugin-protocol. Catalog CID blake3-512:1e5cfee6...17d0579f. Same foundation key. Any v1.1.0 memento or .proof remains valid.
  • New normative spec: protocol/specs/2026-04-30-protocol-catalog-format.md. Defines the two distinct hashing rules (raw bytes for spec files; JCS-canonical for the catalog) and bootstraps recursively (this spec is itself in the catalog).

Five peer-language impls (lib + kit)

Each peer produces byte-identical canonical IR for the same proposition, signs under the same foundation key:

  • Rust: 67 self-contracts → blake3-512:b692f43a...78434dfe
  • Go: 47 self-contracts → blake3-512:906fa4f3...23487ac3
  • C++: 40 self-contracts → blake3-512:52bc62f7...cd323ae0
  • TypeScript: 59 self-contracts → blake3-512:44933993...4904d44
  • C# (foundation Phases 1+2 landed; Phase 3 self-contracts in flight via separate agent): byte-match for x > 0 fixture against C++ peer

Cross-language unicode atomic predicate conformance fixture (≥, ≤, ≠) added to C++ + Go encoders per protocol-catalog-format §5.

Lift-plugin protocol + dispatcher

provekit mint reads .provekit/config.toml, resolves .provekit/lift/<surface>/manifest.toml, spawns the plugin with --rpc, exchanges JSON-RPC over stdio, captures the response, writes <filename_cid>.proof. Three reference CLI plugins working end-to-end:

cd implementations/rust  && provekit mint  # b692f43a...
cd implementations/go    && provekit mint  # 906fa4f3...
cd implementations/cpp   && provekit mint  # 52bc62f7...

Each CID matches what the corresponding direct binary produces. The protocol seam works.

TypeScript is library + kit + toolchain (vitest plugin + zod/class-validator/fast-check/JSDoc adapters), not a CLI peer. The provekit CLI is exclusively Rust.

LINQ lifter (the trojan-horse existence proof)

implementations/csharp/Provekit.Lift.Linq/. Roslyn-based. ~200 LOC. 9/9 tests passing.

  • single_where: xs.Where(x => x > 0) lifts to IR-JSON byte-identical to the kit-authored forall(_x0, implies(gt(_x0, num(0)), member(_x0, positives))).
  • chained_where: users.Where(...).Where(...) produces a 2-memento DAG with InputBindings edges (the chain demonstration).
  • groupby_sum: orders.GroupBy(o => o.Region).Select(g => g.Sum(...)) produces the chain-DAG apex case.

Every existing C# codebase using LINQ becomes a ProvekIt source with zero developer annotation effort. The same shape applies to TS Array combinators, Python comprehensions, Java Streams, Kotlin collection ops, Rust iterators.

Layer 2 lift adapters

Bounded loops + helper-fn inlining + multi-assertion characterization landed for Rust (#[test]), TypeScript (vitest), Go (testing.T), Python (pytest + unittest + parametrize bonus).

Showcase + bluepaper + manifesto

  • docs/launch/bluepaper.md — formal protocol document. Opens with "ProvekIt constrains the set of all possible validations to 64 bytes regardless of length. The proof follows."
  • docs/launch/whitepaper.md — accessible executive summary
  • docs/launch/showcase-results.md — empirical lattice numbers (1.1M mementos, Tier 1 p50 = 58 ns, Tier 2 p50 = 66 µs, Tier 3 p50 = 23.6 ms)
  • docs/launch/why-plugin-lifters.md — manifesto: "the plugin lifter is the only architectural element that lets the bounded substrate serve the unbounded universe"

Test plan

  • cargo run --release --manifest-path tools/recompute-spec-cids/Cargo.toml -- --verify — exits 0
  • ./target/release/provekit verify-protocol --signedstatus: match
  • cd implementations/rust && provekit mint --quiet — produces b692f43a...78434dfe
  • cd implementations/go && provekit mint --quiet — produces 906fa4f3...23487ac3
  • cd implementations/cpp && provekit mint --quiet — produces 52bc62f7...cd323ae0
  • pnpm vitest run implementations/typescript/src/bin/mint-ts-self-contracts.test.ts — produces 44933993...4904d44
  • cd implementations/csharp && dotnet test — 41/41 (foundation) + 9/9 (LINQ lifter) passing
  • tools/build-cpp-self-contracts.sh /tmp/cpp-out — determinism check OK

Outstanding

TSavo and others added 30 commits April 30, 2026 15:00
…ubbed

Lay the groundwork for `provekit lift <file>.ts`: synthesize a signed
.proof from existing TypeScript with no hand-authored must() calls.

This run lands the design doc + adapter scaffold only. Detect is real
and tested; Propose / Filter / Review are stubbed with stable interfaces
so run-2 fills in real LLM, vitest, and TTY wiring without restructuring.
Mint throws loudly until run-2.

Files added:
- docs/superpowers/specs/2026-04-30-provekit-lift-v0.md (design)
- implementations/typescript/src/proveLift/
    index.ts                        public API + adapter registry
    tsPrimitiveAdapter.ts           v0 adapter (TS, primitive types)
    detect.ts                       Stage 1 (real)
    detectSort.ts                   raw TS-type to IR-sort mapping
    propose.ts                      Stage 2 (stub + intake-prompt loader)
    filter.ts                       Stage 3 (stub)
    review.ts                       Stage 4 (stub)
    mint.ts                         Stage 5 (stub; throws)
    errors.ts                       typed diagnostics + LiftError
    prompts/intake.md               LLM intake prompt as editable prose
    __fixtures__/parseInt.ts        positive fixture
    __fixtures__/multiExport.ts     negative fixture
    __fixtures__/nonPrimitive.ts    negative fixture
    __fixtures__/noExports.ts       negative fixture
    tsPrimitiveAdapter.test.ts      13 passing tests + 4 todos

Acceptance gates explicitly marked it.todo for run-2/run-3:
- Mint produces propertyHash 8c38f05152707736 (CID-equivalent to the
  hand-authored fixture at scripts/output/parseInt-mementos/).
- Cross-language round-trip TS / Go / C++ verifiers.

Tests: 13 new passing in src/proveLift; full-suite typecheck clean.
No package deps added.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The earlier scaffold quantified `forall <param>: <ParamSort>`, which
makes the run-2 CID-equivalence gate impossible by construction. The
hand-authored parseInt fixture (propertyHash 8c38f05152707736)
quantifies over Int and reaches String via the `String(n)` coercion
in the body, not via the binder. Lift expresses what must hold of the
function's OUTPUT.

Changes:
- buildQuantifierShape now picks the return sort as the binder.
- Test asserts `forall n: Int` for parseInt(s: string): number.
- Design doc Stage 2 now states the rule explicitly and notes the
  legal-sort universe (return sort + param sorts + registry coercions).
- Added Open Question 3 (prompts/intake.md packaging in dist/) -
  caught by review; tsc does not emit non-TS files, so the runtime
  path needs a build-step or inline-string fix in run-2.

Tests: 13/13 still pass; CID-equivalence gate stays it.todo for run-2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…hake algorithm + maximal IR cleanup

Scorched-earth protocol cut. No legacy aliases. No transitional
shims. Old verifiers refuse new bytes loud.

CONTRACT MEMENTO (replaces property memento)

  evidence.kind: "contract" (was "property-declaration")
  evidence.body: { contractName, pre?, post?, inv?, outBinding,
                   preHash?, postHash?, invHash?, authoring }

  - At least one of pre/post/inv MUST be present.
  - outBinding names the free variable that `post` uses to refer to
    the function's return value (conventionally "out").
  - preHash/postHash/invHash are DERIVED from the corresponding
    formula via hash16(canonical(...)). The redundancy makes the
    handshake-index O(1) per contract at load time.
  - propertyHash = hash16(canonical({pre?, post?, inv?, outBinding}))
    — the whole-contract identity, omitting authoring/contractName.
  - bindingHash = hash16(canonical({producerId, contractName,
    propertyHash})).

IMPLICATION MEMENTO (new role)

  evidence.kind: "implication"
  evidence.body: { antecedentHash, consequentHash, antecedentCid,
                   consequentCid, antecedentSlot, consequentSlot,
                   prover, proverRunMs, smtLibInput?, proofWitness? }

  - A signed Z3 (or other-solver) witness that one IR formula
    universally implies another.
  - Verifiers mint these when Tier 3 of the handshake algorithm
    discharges via solver. Future verifiers hit Tier 2
    (cached implication) instead.
  - inputCids has exactly 2 entries: antecedent + consequent
    contract CIDs.
  - Indexable by (antecedentHash, consequentHash) for the implication
    server pattern.

HANDSHAKE ALGORITHM (new spec: 2026-04-30-handshake-algorithm.md)

  Three-tier discharge for each (post-formula, pre-formula) pair:
    Tier 1: hash equality. Free.
    Tier 2: implication memento cache. One signature verification.
    Tier 3: Z3 fallback. Mint a new implication memento on success.

  Most call sites discharge for free (Tier 1) or cached (Tier 2).
  Z3 fires only on novel pairs. Cost amortizes across the entire
  ecosystem because Tier 3 success publishes a memento that becomes
  Tier 2 for everyone else.

  Reporting includes the breakdown:
    M discharged-by-hash, K cached, L solved, J residue, V violations.
  M / N is the headline metric: contracts compose well when high.

IR MAXIMAL-UNIFORMITY CUT

  Every node has `kind`, then `name` (when applicable), then payload.
  Five formula kinds, three term kinds, one sort kind. Reader holds
  the entire IR in their head.

  Changes:
  - QuantifierFormula flat: { kind, name, sort, body }.
    No more Lambda wrapper inside forall/exists.
  - ConnectiveFormula unified: { kind, operands }.
    `not` (1 operand), `implies` (2), `and`/`or` (2+).
    No more conjuncts/disjuncts/antecedent/consequent/body keys.
  - AtomicFormula: { kind, name, args }.
    Field renamed `predicate` → `name` for uniformity.
  - VarTerm: { kind, name }. Sort dropped (derived from binder).
  - CtorTerm: { kind, name, args }. Sort dropped (derived from
    ctor signature).
  - ConstTerm: { kind, value, sort } unchanged (no derivation source).
  - varName retired everywhere; `name` is the universal field.

  All previously-derived propertyHashes are invalidated. The IR
  shape change is the largest blast radius in the cut; cross-lang
  demos and PoCs re-derive CIDs.

PER-LANGUAGE KIT STANDARD

  - Required export `contract(name, { pre?, post?, inv?, outBinding? })`.
  - Convenience alias `must(name, formula)` ≡
    `contract(name, { pre: formula })`.
  - Required export `out()` — references the function's return
    value within a `post` formula; compiles to a VarTerm whose
    `name` matches the enclosing contract's `outBinding`.
  - Required export `eq` / `gt` / `gte` / `lt` / `lte` / `ne`.

LEGACY-WITNESS REMOVAL

  The legacy-witness wrapper variant existed for transitional
  pre-v1.0.1 mementos. Under scorched earth: removed entirely.
  Producers MUST emit role-specific variants. Validators MUST
  reject legacy-witness as malformed.

VERSION

  Catalog v1.1.0-2026-04-30. Spec CIDs marked RECOMPUTE in the
  catalog will be derived from canonical bytes after the
  implementation cut lands. Catalog signs after that.

NEXT (Stage 2 — separate commit)

  - Update three kits (TS / Go / C++) to emit the new IR + contract
    + implication shapes.
  - Update three minters: mintContract / mintImplication.
  - Update three verifiers: read body.pre/post/inv; implement
    handshake algorithm; mint implications on Tier 3.
  - Rewrite five PoC files: parseInt_kit_proof.cpp, go-kit-publish,
    cross_lang_consumer.cpp, cross-lang-end-to-end.test.ts,
    cross_lang_demo_test.go.
  - Re-run all three cross-lang round-trips green; new CIDs.
  - Recompute spec CIDs; bump catalog; sign.
Stage 2 of the scorched-earth cut — C++ end-to-end on the new
contract memento + maximal IR shape. Cross-lang demo round-trips
green: parse_int(5) discharged, parse_int(0) unsatisfied.

ir.hpp (kit):
- VarTerm: drops sort. Just kind + name.
- CtorTerm: drops sort. kind + name + args.
- AtomicFormula: field renamed predicate -> name.
- ConnectiveFormula: new struct. Unifies and/or/not/implies under
  a kind tag + operands array. Replaces five different field-name
  shapes (conjuncts/disjuncts/body/antecedent/consequent).
- QuantifierFormula: new struct. Flat shape: kind + name + sort +
  body. No nested LambdaFormula. forall() + exists() helpers.
- New atomic helpers: lt, lte, ne (and ≤/≠ unicode).
- New connective helpers: and_, or_, not_, implies.
- New out() primitive: VarTerm with name="out" for post formulas.
- ContractDecl replaces PropertyDecl. Carries pre/post/inv (each
  optional, at least one required) + outBinding (default "out").
- contract(name, pre?, post?, inv?, outBinding?) authoring primitive.
- must(name, formula) retained as the precondition-only alias.
- marshal_declarations emits {kind:"contract", name, outBinding,
  pre?, post?, inv?} with pre/post/inv omitted when null.

claim-envelope/mint.{hpp,cpp}:
- mint_property -> mint_contract. Body shape:
  {contractName, outBinding, pre?, post?, inv?, preHash?, postHash?,
   invHash?, authoring}. Per-formula hashes are DERIVED, not caller-
  supplied. propertyHash + bindingHash also DERIVED per the spec's
  contract-role construction.
- mint_implication: new role. Body shape per spec:
  {antecedentHash, consequentHash, antecedentCid, consequentCid,
   antecedentSlot, consequentSlot, prover, proverRunMs,
   smtLibInput?, proofWitness?}. inputCids = [ant, cons] lex-sorted.
- AuthoringKind enum: KitAuthor / Lift / Llm. Each has its own typed
  args struct in MintContractArgs.
- mint_bridge unchanged in shape; bindingHash + propertyHash now
  DERIVED per the spec's bridge-role construction.

claim-envelope/value_from_kit.{cpp}:
- formula_to_value emits the new uniform IR: var has no sort, ctor
  has no sort, atomic uses `name` not `predicate`, quantifier is
  flat (no Lambda), connectives use `operands` array.

verifier/* (rewrites):
- enumerate_callsites: walks contract.body.{pre,post,inv} (was
  body.irFormula). Walks new IR shape: connectives via operands,
  quantifiers flat, atomic via name.
- resolve_target: kind == "contract" (was "property"); reads
  body.pre as the discharge target (handshake-pre tier).
- instantiate: substitutes through new IR shape. Quantifier var
  via `name` field directly (no nested predicate.varName).
- smt_emitter: atomic uses `name`, connectives use `operands`,
  quantifier via flat fields. Free-var collection updated.

example/parseInt_kit_proof.cpp + cross_lang_consumer.cpp:
- Switched from MintPropertyArgs to MintContractArgs. Caller no
  longer supplies binding_hash/property_hash; minter derives.
- Still uses must() — works as alias for contract({pre: formula}).

Smoke:
  $ /tmp/parseInt_kit_proof_test /tmp/cpp-kit-out-v11
    contract minted: parseInt -> CID 55f6790defb0f43f440f50fab4eee5e0
    bridge   minted: parseInt -> CID 44d39a4e606453f2d1399df0017aeccf
    wrote .proof: bfe74d1a9d836f926058b331002da2f5.proof (1682 bytes)

  $ /tmp/cross_lang_consumer_v11 .../bfe74d1a....proof
      calls-parseInt-with-positive-5: discharged
      calls-parseInt-with-zero:        unsatisfied
    ✓ DEMO: C++ verifier caught parse_int(num(0)) using
      the published precondition.

Next: Go and TS implementations follow the same grammar in
parallel (dispatched as background agents).
Mirrors the C++ reference port (commit 345a719) for the contract
memento + maximal-IR cut. Scorched-earth: no legacy aliases.

IR (kit): atomic uses `name` (not `predicate`); var/ctor drop their
`sort` field from JSON; const keeps it. Quantifiers are FLAT
({kind, name, sort, body}); the Lambda wrapper is gone. Connectives
unify under `operands` (and/or/not/implies); conjuncts/disjuncts/body/
antecedent/consequent are gone.

Authoring: `Contract(name, ContractArgs{Pre, Post, Inv, OutBinding})`
is the full primitive; `Must(name, formula)` is the precondition-only
alias. ContractDeclaration replaces PropertyDeclaration; at least one
of Pre/Post/Inv must be non-nil (panic at collection time, error at
mint time).

Minters: MintProperty is gone. MintContract derives preHash, postHash,
invHash, propertyHash, bindingHash per spec. MintBridge derives
bindingHash + propertyHash (caller no longer supplies). MintImplication
is new — bindingHash = hash16(canonical({antHash, conHash})),
propertyHash = hash16("implication:" || antHash || ":" || conHash).
SchemaCIDProperty renamed → SchemaCIDContract; SchemaCIDImplication added.

Verifier: enumerate-callsites walks contract.body.{pre,post,inv} (was
body.irFormula); kind check is now "contract" everywhere. Connectives
walked via `operands`; quantifiers via flat shape. SMT emitter reads
atomic `name`, builds (forall ((name sort)) body) from the flat shape.
Resolved property targets the `pre` slot.

Cross-lang demo passes: Go consumes the C++ v1.1.0 .proof at
/tmp/cpp-kit-out-v11/bfe74d1a9d836f926058b331002da2f5.proof, verifies
the parseInt bridge, discharges parseInt(5), catches parseInt(0).
Bridge bindingHash + propertyHash reproduce byte-equally across C++
and Go (75f2f878057cc958 / 5c2027d4d05c5fdd) — derivation is locked.

Go publisher emits a v1.1.0 .proof at
/tmp/go-kit-out-v11/6c08bbd16d31d8a8da84624c5f87b3b0.proof
containing one contract memento (CID d63079575fe32297660d48a412255b62)
and one bridge memento (CID fc6966d568ed72d0d5f6aceb9acef38e).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per Sir's stated style preference: no em-dashes (or en-dashes) in
prose. Substituted ` ; ` semicolon-space in most cases; rewrote a
handful where the surrounding sentence read awkwardly. Comments-only
change; no behavior diff. Tests still green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Scorched-earth follow-up to the contract memento + IR cleanup commit.
Cryptographic primitives become self-identifying so future migration
(post-quantum signatures, alternative hash functions) is additive
rather than breaking.

HASH WIDENING

  All hashes: blake3-512:<128-hex-char-digest>
  Format:     <algorithm>-<bits>:<lowercase-hex>

  No truncation. No per-purpose lengths. No prefix-16 / prefix-32
  distinction. The hex16/hex32 type definitions are removed; one
  regex (^[a-z0-9]+-[0-9]+:[0-9a-f]+$) governs every hash field.

  Every place a hash appears — bindingHash, propertyHash, preHash,
  postHash, invHash, antecedentHash, consequentHash, cid, member CIDs,
  schema CIDs, target_contract_cid, depends-on, inputCids — gets the
  full BLAKE3-512 digest with the algorithm prefix.

  Filenames: blake3-512:<hex>.proof. ~150 chars.

SIGNATURE / KEY SELF-IDENTIFICATION

  Signatures: ed25519:<base64>
  Pubkeys:    ed25519:<base64>

  Same migration story. Future post-quantum signatures (Dilithium,
  SPHINCS+) drop in alongside Ed25519 with their own algorithm tags;
  verifiers dispatch by tag at verification time.

ALGORITHM TAGS IN PROTOCOL CATALOG

  protocol-catalog.json gains an `algorithms` field:

    "algorithms": {
      "hash": ["blake3-512"],
      "signature": ["ed25519"],
      "pubkey": ["ed25519"]
    }

  Future catalogs add tags additively. Old bytes never invalidate;
  they just keep their tags.

WHY BLAKE3 OVER SHA-2

  - 10x faster than SHA-512 in software (no hardware-accel disparity)
  - Internal Merkle tree aligns with our content-addressing thesis
  - No length-extension vulnerability
  - Modern construction; "right hash for content-addressing in 2026"

  SHA-2 / SHA-3 can be added as additional permitted tags later for
  FIPS-mandated buyers; the protocol stays correct either way.

WHY 64 BYTES (512 BIT)

  - Post-quantum collision resistance: ~2^170 (vs ~2^85 for SHA-256)
  - Security parameter 256 bits classical
  - "Build once, never re-cut for cryptographic strength"
  - Cost: ~32 extra bytes per hash field. Negligible on a 2KB envelope.

LOOK-NO-FURTHER

  The hash IS the trust AND tells you how to check it. There are no
  separate "version" fields, no "trust me about which algorithm I
  used" assertions. Every cryptographic primitive is self-describing.
  The verifier dispatches on the tag; the hash compares verbatim;
  no further lookup is meaningful or possible.

NEXT (Stage 3 implementation)

  - C++ port: replace sha256_hex(...).substr(0, 32) with
    "blake3-512:" + blake3_512_hex(canonical). Recompute all CIDs.
  - Go port: same.
  - TS port (after current v1.1.0 port lands): same.
  - Rust greenfield impl: builds straight to v1.1.0 with BLAKE3-512
    from day one (no SHA-256 phase).
  - Cross-lang demos re-run; new CIDs.
  - Catalog re-bake with computed spec CIDs; sign.
Port the TypeScript implementation to the contract-memento +
maximal-IR cut. Companion to commits 5448f9a (specs), 345a719 (C++
reference impl), and f378dff (Go reference impl).

IR JSON shape — flat, uniform, sortless:
  - Var/Ctor terms drop sort entirely (kit tracks return sort via a
    non-enumerable Symbol-keyed side channel; off the wire).
  - Const term keeps sort (literals are not derivable from binders).
  - Quantifiers flat: {kind, name, sort, body} — no Lambda wrapper.
  - Connectives uniform: {kind, operands} — no conjuncts/disjuncts/
    body/antecedent/consequent.
  - Atomic field renamed predicate -> name to align with the rest.

Memento envelope:
  - PropertyEvidence + LegacyWitnessEvidence dropped.
  - ContractEvidence added: {contractName, outBinding, pre?, post?,
    inv?, preHash?, postHash?, invHash?, authoring}. preHash/postHash/
    invHash and the wrapper-level propertyHash + bindingHash are all
    derived inside mintContract from the formulas; callers no longer
    pass them.
  - ImplicationEvidence added with mintImplication primitive.
  - Authoring is a typed union: kit-author / lift / llm.
  - Validators reject kind:"property" and kind:"legacy-witness" loud.

Authoring API:
  - contract(name, {pre?, post?, inv?, outBinding?}) is the primary
    primitive; default outBinding = "out".
  - must(name, pre) retained as the precondition-only convenience.
  - out() returns a VarTerm matching the enclosing contract's
    outBinding (default "out").

Acceptance:
  - tsc --noEmit passes from the workspace root.
  - cross-lang-cpp-proof.test.ts decodes the C++ kit's v1.1 .proof
    (/tmp/cpp-kit-out-v11/bfe74d1a9d836f926058b331002da2f5.proof),
    verifies CIDs + signatures, and recomputes preHash from the
    C++-published formula to confirm JCS canonical-encoding agreement.
  - cross-lang-end-to-end.test.ts: TS consumer mints contract
    mementos that reference the bridged C++ parseInt; runner
    discharges parseInt(num(5)) and catches parseInt(num(0)) via
    the C++-authored precondition.
  - 670/670 tests pass, no regressions.

Coverage note: parse.test.ts shed the cross-language fixture
round-trip suite (FIXTURES = ["forall_int_gt_zero", ...]) that
shelled out to scripts/cross-lang-equivalence/ts-runner.ts; that
runner is pre-v1.1 and would need its own update to participate.

Deviation from C++ reference: kit primitives carry the BV operand
sort via Symbol.for("provekit.ir.sortHint") on the in-memory term
object so chains like bvxor(bvadd(x,y), z) keep type-checking
against the lifted spec. The Symbol key is invisible to JSON.stringify
and to the JCS encoder, so the wire shape is identical to C++'s
ctor-without-sort.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Greenfield Rust peer impl scoped to canonicalizer + proof-envelope +
claim-envelope + ir-symbolic kit + a parseInt_publish example. Verifier
crate scaffold present (filled in next commit).

- BLAKE3-512 only. Self-identifying hashes (blake3-512:<128 hex>) and
  signatures (ed25519:<base64>) per the v1.1.0 cut. No sha256, no
  truncation anywhere.
- JCS-JSON encoder mirrors implementations/cpp/.../jcs.cpp 1:1.
- Deterministic CBOR builder mirrors .../proof_envelope.cpp 1:1.
- Kit authoring API: contract/must/forall/exists/and_/or_/not_/implies/
  eq/ne/gt/gte/lt/lte/num/str_const/parse_int/out, with Rc<Formula>/
  Rc<Term> sharing.
- mint_contract / mint_bridge / mint_implication derive preHash /
  postHash / invHash / propertyHash / bindingHash per spec; never
  accept caller-supplied hashes.
- example/parseInt_publish writes a real .proof to argv[1] and prints
  the full self-identifying CID.
- Stale 0.x ir-symbolic skeleton removed (greenfield).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the SHA-256-prefix-32 hash path with BLAKE3-512 self-identifying
hashes throughout the C++ surface, per protocol/specs/2026-04-30-*.

- Drop provekit/canonicalizer/sha256.{hpp,cpp}; add hash.{hpp,cpp}
  wrapping the official BLAKE3 C library (system-linked via -lblake3,
  brew install blake3). Public API: blake3_512_hex(bytes) and
  compute_cid(bytes) -> "blake3-512:" + 128 hex chars (full digest, no
  truncation).
- property_hash, mint (contract / bridge / implication), proof_envelope,
  load_all_proofs, parseInt_kit_proof, cross_lang_consumer all rewired
  to compute_cid. Bridge / implication / property hashes are full
  BLAKE3-512 with the self-identifying tag.
- Signatures on minted envelopes now emit "ed25519:" + base64(sig) per
  the v1.1.0 self-identifying signature format.
- Verifier load_all_proofs.cpp filename regex tightened to
  ^(blake3-512:[0-9a-f]{128})\.proof$, with a fallback that rejects
  any other algorithm tag loud. Member CIDs and producerSignature
  strings are tag-checked on the way in; unknown tags fail verification
  per the v1.1.0 verifier-dispatch contract.
- canonicalizer_test.cpp expected propertyHash regenerated from the
  spec-frozen JCS bytes through BLAKE3-512:
  blake3-512:c592...23a5
- proof_envelope_test.cpp rewritten as a v1.1.0 self-conformance smoke
  test (filename-CID shape, determinism, trust-root coherence). The TS
  reference is being ported in parallel; cross-language byte-pinning
  re-lands when TS catches up.
- tools/run-cross-lang-consumer.sh and tools/run-proof-envelope-
  conformance.sh updated to add -lblake3 + BLAKE3_PREFIX include path
  and to swap sha256.cpp for hash.cpp. The cross-lang script tolerates
  a Go publisher that hasn't ported v1.1.0 yet (build-only mode).

Local test status:
  - canonicalizer conformance test PASS (BLAKE3-512 propertyHash matches)
  - proof_envelope smoke test PASS (filename CID + determinism + trust)
  - C++ publisher -> C++ verifier round-trip GREEN: 2 mementos loaded,
    0 errors, contract + bridge resolved.
Mirrors the C++ and Go verifier shape: load_all_proofs (with v1.1.0
trust-root + tag-dispatch checks for blake3-512: and ed25519:),
enumerate_callsites (walk pre/post/inv for ctor terms matching known
bridge sourceSymbols), resolve_target (return target contract's pre as
discharge IR), instantiate (substitute call's arg into flat-quantifier
body), smt_emitter (set-logic ALL + free-var declarations + assert
(not body) + check-sat), solve_obligation (z3 subprocess with
unsat -> Discharged / sat -> Unsatisfied / other -> Undecidable),
report (per-callsite verdict aggregation).

Per-callsite stages 3-5 fan out via rayon (mirrors std::async + goroutines).

Added rust_round_trip example: reads a directory of .proof files, runs
load + enumerate + per-callsite resolve, asserts every enumerated
callsite resolves cleanly. Verified end-to-end against the
parseInt_publish example output.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds 87 new tests across the two foundational Rust crates.

provekit-canonicalizer (+51 tests):
- jcs_extended.rs (30 tests): RFC 8785 invariants — sort order at
  multiple levels, ASCII byte order, control-character u00XX escapes,
  forward-slash NOT escaped, empty / nested objects + arrays, large
  string smoke, determinism over reorderings. Documents the multi-byte
  UTF-8 double-encode bug in jcs.rs:84 with a deterministic-but-wrong
  pin so a future fix is flagged loud.
- hash_known_answer.rs (10 tests): BLAKE3 KAT against the official
  test_vectors.json (input_len 0..7) plus avalanche / no-collision
  / 1MB smoke. Empty-input answer is the published BLAKE3 OKM.
- cid_invariants.rs (11 tests): self-identifying-tag prefix, exactly
  128 lowercase hex chars, regex compliance, length independent of
  input length.

provekit-proof-envelope (+57 tests):
- cbor_deterministic.rs (26 tests): RFC 8949 4.2.1 shortest-form uint
  at every length boundary (0/23/24/255/256/65535/65536/u32::MAX/u64::MAX),
  bstr vs tstr major-type discrimination, array + map heads at the 24
  boundary, no indefinite-length leakage.
- ed25519_signing.rs (17 tests): determinism, distinct-seed/message
  sensitivity, ed25519: prefix, 88/44 base64 char lengths, signature
  verifies under correct key, fails under wrong key, fails on tampered
  message, empty + 100KB message round-trips.
- proof_envelope_catalog.rs (14 tests): trust-root rule (filename CID
  == BLAKE3-512(catalog bytes)), 7-key map head, determinism across
  member-insert orders, sensitivity to every input field.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Rust impl now eats itself. New crate `provekit-self-contracts`
authors 7 contracts about the impl's IR-JSON parser, mints them as a
.proof file, and the verifier scans them. The framework is its own
first user.

Adds a typed IR-JSON parser at `provekit-ir-symbolic/src/parse.rs`.
The kit had a serializer (Formula -> JCS) but no parser; this closes
the round-trip. Closed-object policy: extra keys on var/ctor/etc are
rejected loud per the v1.1.0 grammar. Arity rules are enforced (not=1,
implies=2, and/or>=2). 11 unit tests cover round-trip + negative
shapes; 2 proptest properties (64 cases each) cover round-trip
identity and determinism on ASCII-predicate atomic formulas.

Adds `provekit-self-contracts` crate split into `author_self_contracts`
(pure) and `mint_self_proof` (writes .proof). Seven contracts:
parse_formula determinism, parse_formula correctness, malformed-input
rejection, compute_cid length, two arity claims, serialize totality.
One bridge memento closes the call-site loop (parse_formula ->
parse_formula_correct), so the verifier's enumerate_callsites stage
finds 3 hits in the dogfood pool.

Adds examples/scan_self.rs. End-to-end run produces 8 mementos
bundled (7 contracts + 1 bridge), 0 load errors, 3 callsites
enumerated, 3 verdicts (all undecidable; kit-defined roundTrips
predicate has no Z3 semantics, which is the honest pipeline outcome).

Findings: the JCS encoder in provekit-canonicalizer mishandles
non-ASCII bytes (treats each UTF-8 byte as a Latin-1 code point at
re-encode time), so gte/lte/ne predicates trip envelope-CID
re-derivation. Self-contracts and proptest are restricted to the
ASCII subset; tracked as a follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
claim-envelope (+47 tests):
- mint_contract.rs (24 tests): EmptyContract / EmptyOutBinding error
  paths; full pre/post/inv combination matrix (7 cases); preHash /
  postHash / invHash DERIVED — caller can't supply, recomputation
  catches forgery; propertyHash and bindingHash derivations match the
  spec formulas independently; CID = blake3-512 of unsigned canonical
  bytes (strip-and-rehash matches); all three Authoring variants
  (KitAuthor / Lift / Llm) round-trip.
- mint_bridge_implication.rs (23 tests): bridge propertyHash =
  blake3-512("bridge:" + sourceSymbol); bindingHash from
  {sourceLayer, sourceSymbol}; inputCids[0] == targetContractCid;
  notes omitted when empty; implication propertyHash from
  "implication:" + ah + ":" + ch; inputCids contain both CIDs
  lex-sorted by the wrapper; smtLibInput / proofWitness omit-when-empty.

ir-symbolic (+51 tests):
- structural.rs (24 tests): collector lifecycle (must / contract /
  finish drains / multi-cycle / begin_collecting); kit's empty-contract
  panic; forall + exists fresh bound names from a shared counter;
  reset_collector resets the counter; not has 1 operand, implies has 2,
  and/or pass through caller arity; Unicode atomic names round-trip
  (>, <, =, NotEq, GreaterEq, LessEq).
- serializer.rs (27 tests): every formula kind serializes to the
  spec-locked Value tree shape; var/const/ctor/atomic/connective/
  quantifier/sort serialization. Cross-language hash anchor: pins the
  exact JCS bytes of forall(Int, n -> n > 0) and asserts the BLAKE3-512
  is reproducible from a hand-built equivalent tree. Hand-builds
  match kit's bound-name "_x0" so this byte sequence is the
  cross-language reference.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ation

Caught by the dogfood scan: ProvekIt's self-verification surfaced a
UTF-8 encoding bug in its own canonicalizer that no Rust type check
could express. Every operation in the buggy code was type-valid:

    for b in s.as_bytes() {       // &[u8]. correct.
        let c = *b;                // u8.   correct.
        out.push(c as char);       // u8 -> char is a valid cast. correct.
    }

But `c as char` interprets each UTF-8 continuation byte as a Latin-1
code point. For ASCII (0x00-0x7F) Latin-1 and UTF-8 agree, so the bug
hid behind every existing unit test. For non-ASCII (0x80-0xFF), the
byte gets re-encoded as multi-byte UTF-8 garbage. Encoding ≥ (U+2265,
bytes E2 89 A5) produced "â¥" in the output — three corrupted Latin-1
chars whose UTF-8 re-encoding doesn't match the input bytes.

The bug breaks Rule-2 envelope CID re-derivation for any contract
using ≥ / ≤ / ≠ — exactly the unicode atomic predicates the kit emits
for `gte` / `lte` / `ne`. Cross-language hash agreement collapses the
moment any kit ships a contract with these atomics.

Fix: iterate over Unicode scalar values (chars), not bytes. Pushing a
char into a Rust String encodes it back as the same UTF-8 bytes the
input carried. Cross-language hash agreement preserved.

Regression tests added:

- `unicode_atomic_predicates_round_trip_verbatim`: ≥/≤/≠ encode
  byte-faithfully (the case that caught the bug).
- `mixed_ascii_and_unicode_preserved`: a string with both ASCII and
  unicode round-trips.
- `unicode_in_object_key_and_value`: a typical IR-JSON shape using a
  unicode atomic name; output bytes match what a sibling impl
  (C++ writing raw UTF-8) produces. Bytes asserted explicitly.

This is the kind of bug ProvekIt catches that no type system can
express. Rust says "your types are right." The contract says "your
bytes don't preserve UTF-8." Different layers; ProvekIt fills the gap.
Adds 5 integration test files covering each verifier stage end-to-end.

provekit-verifier (+71 tests):
- smt_emitter.rs (27 tests): every formula kind translates correctly;
  Unicode atomic predicates (NotEq -> distinct, LessEq -> <=,
  GreaterEq -> >=); free-variable collection respects shadowing;
  emitted script has set-logic ALL header, asserts negation, ends
  with check-sat; bad inputs (unknown kinds, missing args, empty var
  names, missing body) all return Err.
- instantiate.rs (14 tests): substitutes through atomic / connective /
  ctor / nested ctor; respects shadowing (inner forall with same name
  blocks substitution beneath it); inner forall with different name
  allows substitution; fail-closed on missing arg, missing ir_formula,
  non-forall resolved formula, missing name or body.
- resolve_target.rs (8 tests): resolves pre for contract memento;
  fail-closed on missing CID, wrong evidence kind (bridge /
  implication / unknown), missing body, missing evidence.
- enumerate_callsites.rs (12 tests): finds ctor in pre/post/inv; under
  quantifier body; inside connective operands; nested ctor-in-ctor
  triggers callsite; non-contract envelopes skipped; ctor-name-with-
  no-matching-bridge skipped; arg term carried into the CallSite;
  property_name falls back to CID prefix when contractName absent;
  multiple callsites in same contract each listed.
- load_all_proofs.rs (10 tests): nonexistent / empty / unrelated-files
  dirs return empty pool with no errors; published parseInt .proof
  loads cleanly with 1 contract + 1 bridge; member CIDs correctly
  prefixed; rule 1 (filename != content hash) rejected with
  "rule 1" error; non-hex filename rejected; garbage CBOR lands in
  load_errors; multiple .proof files all loaded; subdirectories
  walked recursively.

Total Rust test count now 327 (was 21 baseline, now spans all six
crates with no crate below 10 unit + integration tests).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- serializer.rs: pin EXPECTED_PARSEINT_PRE_HASH constant so the
  forall(Int, n -> n > 0) anchor locks the IR-shape x JCS-encoder x
  BLAKE3-512 composition end-to-end. Cross-language peers must
  reproduce this exact 128-hex digest; any regression in any of the
  three layers flips it loud.
- jcs_extended.rs: replace the deterministic-but-buggy UTF-8 pin
  (written before commit c4a2ef5 fixed jcs.rs) with a real
  byte-faithful round-trip assertion.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the SHA-256-prefix-{16,32} hash path with BLAKE3-512
self-identifying hashes throughout the Go surface, per
protocol/specs/2026-04-30-canonicalization-grammar.md and
2026-04-30-memento-envelope-grammar.md.

- Drop sha256 + truncated-prefix helpers from canonicalizer/hasher.go;
  replace with BLAKE3-512 wrapper backed by lukechampine.com/blake3.
  Public API: ComputeCID(canonical []byte) string returns
  "blake3-512:" + hex(BLAKE3_512(canonical)) with full 128 hex chars.
- claim_envelope: contract / bridge / implication minters now derive
  preHash / postHash / invHash / propertyHash / bindingHash via
  ComputeCID. Schema CIDs lifted to full v1.1.0 self-identifying form
  (matches C++ mint.cpp constants verbatim).
- Signatures on minted envelopes emit "ed25519:" + base64(sig) per the
  v1.1.0 self-identifying signature contract.
- proof_envelope/builder.go: filename CID switches to ComputeCID
  (full BLAKE3-512 with "blake3-512:" prefix). The catalog's raw-byte
  signature field stays a CBOR bstr per spec.
- verifier/load_all_proofs.go: filename regex tightened to
  ^(blake3-512:[0-9a-f]{128})\.proof$, with a fallback that rejects
  any other algorithm tag loud ("unsupported hash tag in filename;
  v1.1.0 requires blake3-512:"). Member CIDs and producerSignature
  strings are tag-checked on the way in; unknown tags fail verification
  with a v1.1.0-conformant error.
- cmd/go-kit-publish: SignerCID switched from "sha256:..." sentinel to
  v1.1.0 "blake3-512:..." sentinel; output filename is now
  blake3-512:<128 hex>.proof.
- verifier/cross_lang_demo_test.go: switched to filepath.Glob discovery
  of /tmp/cpp-kit-out-v11/*.proof since the C++ filename CID changed
  shape under v1.1.0. Test passes against the re-published C++ proof:
  discharges parseInt(5), catches parseInt(0) using the C++-authored
  precondition.
- go.mod: lukechampine.com/blake3 v1.4.1 (well-maintained pure-Go
  reference). go.sum committed.

Local test status:
  - go build ./... clean
  - go test ./... PASS (ir + verifier)
  - go run ./cmd/go-kit-publish /tmp/go-kit-out-v11 produces:
    blake3-512:8374054da3e741b2c59002d1aab0cc726185222a402042e754407a676d4c32696fb8e38040c9f49874fc4ce84b91ce8f33fb4adf298381671e2b75ef102a5f97.proof
  - cross-lang Go-verifies-C++ test PASS: 1 discharged + 1 violation,
    Go verifier uses C++-authored parseInt precondition.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Proves that ProvekIt's verification problem lives in the IR-grammar-bounded
search space L_C(G, D, S), not in the 2^512 BLAKE3-512 address space. Honest
verifier cost is decoupled from the cryptographic security parameter; adversarial
cost is confined to cryptographic preimage / collision attack. Six numbered
claims (full proofs for 1, 3, 4, 6; sketches for 2, 5), four corollaries, and
honest open work on tight population bounds, empirical density, and edge
composition.

Catalog entry added with RECOMPUTE placeholder.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace every blake3-512:RECOMPUTE-AFTER-* placeholder in the
protocol catalog with the actual BLAKE3-512 CID of the spec doc's
raw bytes, then compute the catalog's own CID over its
JCS-canonical form (RFC 8785).

Protocol version CID (v1.1.0):
  blake3-512:67961c8772930809589adb795c4bfe4104e6510cbafe7ed57d9dd8ce598eee5888d1e4b037b637e22f91a2ca5636188529e9d2451139c264691c5624c31d9cda

Updates protocol-versioning.md:
- Replace stale sha256 v1.0.0 entries with full BLAKE3-512 CIDs
- Add the four new spec entries (proof-file-format,
  handshake-algorithm, per-language-kit-standard,
  lattice-tractability-theorem)
- Document explicitly that the catalog CID is computed over the
  JCS-canonical form, not the human-readable file bytes

Adds tools/recompute-spec-cids/, a small Rust binary that reads
every spec, hashes it with BLAKE3-512, substitutes CIDs into the
catalog, and computes the catalog CID via provekit-canonicalizer's
encode_jcs (the same encoder the Rust peer uses, guaranteeing
cross-language hash agreement). Run with --verify to recompute and
fail on drift; default mode rewrites the catalog in place.

Catalog signing remains a separate operational task pending the
project root key.
Bring the Rust impl into the four-way conformance demonstration alongside
C++/Go/TS. Rust now has a cross-language consumer example that verifies
peer-published .proof files, and the existing TS/Go cross-lang tests are
generalized to walk every peer publisher dir (cpp / go / rust) so each
cell of the matrix reports independently.

  Cell             Status
  cpp -> ts        PASS (existing, generalized)
  cpp -> go        PASS (existing)
  cpp -> rust      PASS (NEW)
  go  -> cpp       PASS (existing)
  go  -> ts        PASS (NEW)
  go  -> rust      PASS (NEW)
  rust -> cpp      PASS (NEW)
  rust -> go       PASS (NEW)
  rust -> ts       PASS (NEW)

Changes:
- implementations/rust/examples/cross_lang_consume.rs: new Rust consumer.
  Loads any peer .proof, authors parse_int(num(5))/parse_int(num(0))
  invariants, mints contract mementos, bundles + signs its own .proof,
  runs the Rust verifier, asserts 2 callsites with 1 discharged + 1
  unsatisfied. Idiomatic Result-based error handling; panics confined
  to the example main.
- implementations/rust/examples/parseInt_publish.rs: write .proof files
  with the v1.1.0 colon-prefixed filename ("blake3-512:<hex>.proof")
  matching the C++/Go peers, so all four langs are mutually discoverable
  by the same regex.
- implementations/rust/provekit-verifier/Cargo.toml: register the new
  cross_lang_consume example so it shares the workspace deps.
- implementations/typescript/src/proofEnvelope/cross-lang-cpp-proof.test.ts:
  generalize from CPP-only to a glob over /tmp/{cpp,go,rust}-kit-out-v11/.
  One subtest per discovered .proof.
- implementations/typescript/src/proofEnvelope/cross-lang-end-to-end.test.ts:
  same generalization for the full Z3 enforcement test.
- implementations/go/provekit-ir-symbolic/verifier/cross_lang_demo_test.go:
  generalize from C++-only; renamed to TestCrossLangGoVerifiesPeerProof
  and split into per-peer subtests via t.Run.
- tools/run-cross-lang-consumer.sh: also feed Rust .proof to the C++
  consumer (cell rust -> cpp). Refresh both Go and Rust publishers.
- tools/run-cross-lang-rust-consumer.sh: new driver that builds the Rust
  consumer and runs it against C++ and Go .proofs.

No conformance bugs surfaced. The existing Rust publisher's hex-only
filename was stale (claimed parity with C++ but C++/Go had moved to
colon-prefixed filenames); fixed in passing so peer discovery works
uniformly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New crate at implementations/rust/provekit-cli/ that wires the
existing Rust libs (canonicalizer, proof-envelope, claim-envelope,
ir-symbolic, verifier) into a clean clap v4 subcommand surface.
Built binary lives at target/release/provekit; cargo install
--path provekit-cli installs it under that name.

Subcommands fully wired:
  prove / verify       runs the six-stage pipeline via Runner
  hash                 BLAKE3-512 of file or stdin
  dump                 pretty-prints .proof catalog members
  ask                  parses IR-JSON formula, hashes, walks proofs
  search               --consequent / --antecedent over implications
  init                 creates provekit.toml + .provekit/ + GHA workflow
  verify-protocol      recomputes embedded catalog CID, compares
  version              prints version + protocol catalog CID
  lift                 honest stub pointing at TS adapter
  implicate / imp      honest stub (needs CID resolver, planned v1.2)

Catalog conformance is wired honestly: assets/protocol-catalog.json
is embedded via include_bytes!, recomputed at runtime, and asserted
against the hard-coded EXPECTED_CATALOG_CID by a unit test that fails
loud on drift between the binary and the protocol pin.

Output: pretty/colored by default (owo-colors), --json on every
subcommand for machine consumption, --quiet suppresses non-error
output. Exit codes follow the spec (0/1/2/3).

22 CLI unit tests; full workspace passes 349.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Author 67 hand-written contracts across 13 .invariant.rs files
(one per public-API source file in the workspace), authored using
the kit's own primitives (`must`, `contract`, `forall`, `eq`, `gt`,
`lt`, `gte`, `lte`, `num`, `str_const`, `atomic_`, `and_`, `or_`,
`not_`, `implies`).

The build orchestrator (`provekit-self-contracts`) walks every
.invariant.rs via `#[path]` so the host crate dep graph stays
acyclic (canonicalizer / claim-envelope / proof-envelope cannot
back-edge into ir-symbolic for the kit's authoring API). The
binary `mint-self-contracts` mints all contracts as signed
mementos plus the closed-loop bridge `parse_formula ->
parse_formula_correct`, bundles into a single `<cid>.proof`
written to `target/release/<cid>.proof` (filename IS the catalog
CID per protocol/specs/2026-04-30-proof-file-format.md), and
asserts byte-determinism by minting twice into separate dirs and
comparing CIDs.

The previous 7 parser contracts (originally inline in
provekit-self-contracts/src/lib.rs::author_self_contracts) have
been relocated to provekit-ir-symbolic/src/parse.invariant.rs
under the per-source-file convention; the orchestrator now
imports them through the same #[path] mechanism. The closed-loop
bridge to `parse_formula_correct` is preserved end-to-end and
still enumerates as 3 callsites against parse_formula references
across `parse_formula_determinism` and `parse_rejects_malformed`.

Honest scope: most .invariant.rs files reach for kit-defined
atomic predicates (roundTrips, isErr, isMalformed,
cidMatchesFilename, bridgeKnownInPool, ...) that Z3 has no
semantics for; those callsites resolve to "undecidable", which is
the protocol's HONEST outcome documented per-file. The
LIVING-DOC value is the spec-shaped contract that travels with
the source. Stronger byte-faithful invariants (RFC 8785 key
order, CBOR shortest-form encoding, ed25519 round-trip,
parse(serialize(f)) == f) live as proptest / known-answer tests
in the per-crate test directories and are documented in the doc
comments above each contract.

Per-file contract counts:
  jcs                  5
  hash                 5
  cbor                 5
  sign                 4
  proof                5
  claim-envelope       9
  parse                7  (relocated from src/lib.rs)
  serialize            6
  load_all_proofs      5
  enumerate_callsites  4
  resolve_target       3
  instantiate          4
  smt_emitter          5
  TOTAL               67  (target was 30+)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…antics spec

Adds the Rust attribute-macro authoring surface as an OPTIONAL sugar
layer on top of the existing kit primitives. Same kit primitives,
different placement.

Two crates:
- provekit-macros (proc-macro = true): #[provekit::contract(pre, post,
  inv, out_binding, name)] and #[provekit::verify] attributes.
- provekit-macros-rt: runtime registration types. Required because
  ContractDecl carries Rc<Formula> which isn't Sync; inventory::submit!
  needs Sync. Solution: register a ContractRegistration { name,
  source_path, source_line, builder: fn() -> ContractDecl } where the
  builder fn pointer is Sync; the decl gets constructed lazily on call.

Spec: protocol/specs/2026-04-30-contract-merge-semantics.md captures
the four resolution cases:
1. Identity match (byte-equal canonical) -> dedupe silently.
2. Orthogonal slots (different aspects) -> merge.
3. Same slot, different formulas -> fail-loud at build time.
4. Cross-language -> no merge; bridges link separately.

Catalog: contract-merge-semantics added with RECOMPUTE placeholder.

Tests: 6 smoke + 2 trybuild (1 pass + 1 compile-fail with snapshotted
.stderr) = 8/8 green.

Note on positioning: this macro layer is FALLBACK-FOR-GREENFIELD, not
the central pitch. The lift-adapter strategy (consume existing
annotation libraries: contracts, prusti, kani, proptest, deal,
Pydantic, Bean Validation, Zod, etc.) is the canonical adoption path.
ProvekIt sits beneath every existing annotation library; we don't
compete with any of them.
After provekit-macros + contract-merge-semantics spec landed, the
catalog grows by one entry. New protocol version CID:

  blake3-512:9d57c5e47083b92e8cc5dab365a718fc0afee6556d34ffe40b303dd7ad4d9caa88dbbc6248e318cc76e57b30a0b2ad49f6f9dbf1916ac164a89df44324d6c106

(Previous: blake3-512:67961c87...c31d9cda; superseded.)

Updates:
- protocol/specs/2026-04-30-protocol-catalog.json: contract-merge-semantics
  entry now has its real BLAKE3-512 CID
  (aeb9e2c56603...8a268bee).
- protocol/specs/2026-04-30-protocol-versioning.md: new catalog CID + table
  entry for contract-merge-semantics.
- tools/recompute-spec-cids/src/main.rs: SPEC_MAP gains
  contract-merge-semantics so future re-bakes find the new spec.
- implementations/rust/provekit-cli/src/protocol.rs: hard-coded
  EXPECTED_CATALOG_CID updated; build-time recompute test passes.
- implementations/rust/provekit-cli/assets/protocol-catalog.json:
  re-snapshotted from canonical spec.

The previous catalog CID never shipped externally; same set of bytes,
internal-only version. The cycle of (add spec, recompute, commit) is
now a one-command flow via `cargo run --release --manifest-path
tools/recompute-spec-cids/Cargo.toml`. Anyone in the world can reproduce
the CID by running the same command on the same spec bytes.
The canonical adoption path for ProvekIt: walk an existing Rust
workspace, find annotations from established libraries, lift each one
to canonical IR, mint signed content-addressed contract mementos, and
bundle into a single .proof catalog. Developers do not rewrite anything.

Three new workspace crates:
- provekit-lift: core walker + cargo subcommand (cargo-provekit-lift)
  + plain provekit-lift binary. Library API: lift_path / mint_proof /
  lift_and_mint.
- provekit-lift-proptest: walks proptest! { #[test] fn ... } blocks.
  Whitelist: prop_assert / prop_assert_eq / prop_assert_ne with binop
  comparison over var/literal/single-arg-call.
- provekit-lift-contracts: walks #[requires] / #[ensures] /
  #[invariant] (and contracts:: prefixed forms). Same predicate
  whitelist.

End-to-end against the workspace lifts 11 contracts (7 proptest, 4
contracts) into a stable .proof CID. The bundle loads cleanly through
provekit-verifier::load_all_proofs — every member envelope passes the
trust-root and CID-redrive checks.

15 tests: 5 lift integration (walk+lift+mint+verifier-load,
content-addressed dedup, name-collision-on-different-IR fail-loud,
CLI smoke), 3 lift unit (CLI flag parse + cargo-arg strip + e2e
fixture), 4 proptest adapter, 3 contracts adapter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The README, ARCHITECTURE, PRODUCT, PITCH, THESIS, and LANDING docs
were all written pre-protocol-cut and described the 9-stage / 18-oracle
TypeScript/Node pipeline that the architecture rewrite obsoleted.

Refresh all six top-level docs around the v1.1.0 canonical claims:
- protocol version IS its hash (catalog CID, anyone can verify)
- petabyte / 64-byte ratio (memcmp is the protocol)
- no database; computable hashspace (Bitcoin/Git/IPFS lineage)
- look-no-further (the hash is the verification barrier)
- librarian, not the expert (content-addressed lookup; Z3 is rare)
- no invalidation; provability is monotonic
- search by content, not by name
- trust built into the protocol; no permission required
- lift, don't author (sit beneath every annotation library)
- 64-byte verification is one CPU instruction
- per-language libs + kits + canonical Rust CLI
- lattice tractability theorem (finite at any complexity bound)
- compile-time errors via build-script integration (planned for v1.2)

Add three new docs:
- docs/getting-started.md: 5-minute install + first .proof
- docs/lift-adoption-paths.md: per-source-library adoption guide
- docs/per-language-status.md: matrix of kits + libs + adapters

Stub docs/QUICKSTART.md to redirect to getting-started.md (the old
content described a Node CLI install path that no longer exists).

All CIDs referenced are pulled from
protocol/specs/2026-04-30-protocol-catalog.json. No spec-file
changes; this commit is doc-only and does not touch the lift/build/
stage4 territories owned by parallel agents.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wires the verifier into Rust's `cargo build` so contract violations
become compile-time errors instead of runtime failures. A consumer
crate adds four lines (`[build-dependencies] provekit-build = "0.1"`,
`[package.metadata.provekit] strict = ...`, plus a one-line
`build.rs`) and gets every `#[provekit::contract]` and
`#[provekit::verify]` annotation gated by Z3 at compile time.

Architecture (per provekit-build/SPEC.md):
- Source-walks the consumer's `src/` tree with `syn::visit::Visit`
  to find contract / verify annotations and intra-body call sites.
- Reads `[package.metadata.provekit]` for strict / mint_proof /
  verify_targets / z3_timeout_ms config; defaults to lax mode.
- Emits SMT-LIB 2 obligations per call site and dispatches to Z3
  with both `:timeout` and a wall-clock guard at 3s.
- Mints a BLAKE3-512-keyed `<cid>.proof` manifest under OUT_DIR.
- Surfaces findings as `cargo:warning=provekit:` lines; in strict
  mode, an unsatisfied call site exits the build script non-zero
  (cargo's equivalent of `compile_error!`).

Tier handshake: this is Tier 3 only (per-callsite Z3). Tiers 1 / 2
(handshake-cached implication mementos) are explicitly out of scope
and live in a separate crate.

Verdict mapping (see provekit-build/README.md for the table):
- post realizable + no surrounding `==` -> Discharged
- post + surrounding `if x == K` is unsat -> Unsatisfied (dead branch)
- post + surrounding `if x == K` is sat -> Discharged
- Z3 unknown / spawn-failure / timeout -> Undecidable

Demo crate at implementations/rust/examples/build_script_demo:
- `abs_value` (post: out >= 0) + `use_abs` -> discharged
- `always_positive` (post: out >= 1) + `deliberate_violation` with
  `if x == 0` -> Z3 returns unsat, surfaces as
  `cargo:warning=provekit: WARN ... violation: branch is dead per
  contract`. Build succeeds (strict=false); flipping strict=true
  fails the build with the same finding.

Tests: 14 integration tests cover config parsing (3), strict-mode
report path (1), mint_proof flag + determinism (2), Z3 subprocess
spawn-failure / timeout config (2), source-walker shape recognition
across gte / gt / eq / opaque (3), call-site enumeration in verify
bodies (1), surrounding-eq detection (1), filesystem walk (1).

Acceptance:
- `cargo build --release -p provekit-build` succeeds.
- `cd examples/build_script_demo && cargo build --release` runs the
  verifier; report shows 2 contracts / 2 verify targets / 2 callsites
  with one discharged and one dead-branch flagged.
- Demo proof CID:
  blake3-512:7e8e6de20d67ad7bbdf8ecfe4117878dd7fe959ecd2de4d319e1ae76dcd29c7701c55c8f23814fc2c0c449476242ffcd1edc32b5ae6a6854a360fa1533f438f9
- Test pass count: 14 / 14.

Spec lives inline at provekit-build/SPEC.md (build-script protocol
notes, tier handshake, SMT encoding, failure-mode table). Not
promoted to protocol/specs/ yet because this layer is
implementation-shaped, not protocol-shaped; lifts when a second-
language peer needs the same shape.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the marketing demonstration for the constraint-driven verifier:
most cross-language verification work goes away because publishers
and consumers either agree on hash, or hit a content-addressed cache
of signed implication mementos.

What ships:

- provekit-verifier handshake.rs: Tier 1 (hash equality) and Tier 2
  (cached signed implication memento) discharge paths. Hash equality
  uses BLAKE3-512 over the JCS-canonical formula bytes; Tier 2 keys
  the cache by BLAKE3("implication:" || post_hash || ":" || pre_hash)
  per the memento-envelope grammar.

- provekit-verifier runner.rs: integrates the three tiers with
  per-tier counters surfaced via a new TierStats struct. On Tier-3
  unsat the verifier mints a v1.1.0 implication memento, signs it,
  and writes it to .provekit/cache/ so the next run becomes Tier-2.
  The minted memento embeds the producer pubkey so any other ProvekIt
  verifier can verify the signature without an external key store.

- provekit-proof-envelope: ed25519_verify_string helper for the
  Tier-2 signature check.

- examples/stage4-handshake-demo/: a four-run scripted demo proving
  cross-language interop in protocol bytes only. Go publishes
  validateInput.post; Rust publishes parseInt.pre; Rust verifies.
  Run A (Tier 1 hit) reports zero Z3 invocations; Run B mints once
  and hits the cache on warm replay; Run C demonstrates content-
  addressed "invalidation" by changing the antecedent hash; Run D
  re-uses the cached memento once the publisher restores its post.

Backwards-compat: RunnerConfig now has three new optional fields
(cache_dir, mint_seed, mint_producer_id). Existing call sites in
scan_self, cross_lang_consume, rust_round_trip, mint-self-contracts,
and provekit-cli are updated to use ..Default::default() so legacy
mode (no handshake) is opt-in via not setting cache_dir.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per launch-post copy-paste hygiene: discharged_by_hash now counts
only real publisher-post / consumer-pre hash matches. Call sites
whose bridged target has no `pre` slot at all (publisher is
post-only) are vacuously discharged but recorded in a new
`vacuous_discharge` counter so the headline metrics quote 1/1
discharged-by-hash for Run A, matching the task spec.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TSavo and others added 5 commits April 30, 2026 22:41
v1.2.0 freezes four pluggability protocols that were drafted but not
yet in a signed catalog:

  agent-plugin-protocol   (LSP-shape JSON-RPC for coding agents)
  ir-compiler-protocol    (per-solver IR translators)
  multi-solver-protocol   (single/chain/portfolio dispatch)
  lift-plugin-protocol    (Rust CLI -> per-language plugins, stdio RPC)

All four are additive over v1.1.0: any v1.1.0 memento or .proof
remains valid under v1.2.0; the bump only adds capabilities,
removes nothing, breaks nothing. v1.1.0's catalog CID
(5b770182...19f05cd4) remains the version anyone pinned to it.

v1.2.0 catalog CID:
  blake3-512:1e5cfee6043d485d276c26a8da17830fe828c5b7b395a5fb1f042e7442407a37c39c59c0e002ca18857b12d3efb0d86687b9a3a0e3f6e3e933856f0717d0579f

v1.2.0 attestation signed under the foundation v0 key
(ed25519:IVL40Zt5HSRFMkLhXy6rbLfP+ntqXtMAl5YOBpiB2xI=); written to
.provekit/catalog-signatures/v1.2.0.json. The signing tool was
extended with build_signed_attestation_for(version, ...) and
signature_path_for(version) so future minor bumps can sign by adding
a thin per-version binary instead of duplicating sign_catalog.

The four draft spec headers stop saying "RECOMPUTE-pending until
v1.2.0 lands" — they declare v1.2.0 normative status. Spec CIDs
themselves are NOT embedded in the headers (self-reference would
flip the bytes; the catalog already carries the values).

Bluepaper §0 cites the new catalog CID; Appendix A and the version
log carry the additive change forward. Any verifier running
recompute-spec-cids -- --verify against the on-disk catalog produces
the value in §0; both v1.1.0 and v1.2.0 attestation artifacts remain
on-disk for cross-check.

Sir's framing locked in:
  Lib    = IR -> .proof (protocol primitives)
  Kit    = host source -> IR (authoring API)
  Toolchain = any lifter the host ecosystem ships (annotation
              libraries, LINQ-style query syntax, test runners,
              custom decorators)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
C# (.NET 10) implementation of the ProvekIt protocol, peer to Rust,
C++, Go, and TypeScript. Phases 1 (library) and 2 (kit-authoring API);
self-contracts (Phase 3) and the [DllImport] bridge demo (Phase 4)
land in follow-ups.

Library:
- Provekit.Canonicalizer  -- JCS-JSON encoder (RFC 8785) + BLAKE3-512
  self-identifying CIDs (full 64-byte XOF, no truncation)
- Provekit.ProofEnvelope  -- deterministic CBOR (RFC 8949 §4.2.1) +
  Ed25519 signing (NSec.Cryptography) + .proof envelope builder
- Provekit.ClaimEnvelope  -- MintContract / MintBridge / MintImplication
  with full hash derivation per spec

Kit-authoring API (Provekit.IR): Must, Contract, ForAll, Exists,
And/Or/Not/Implies, Eq/Gt/Gte/Lt/Lte/Atomic (≥, ≤, ≠ as UTF-8),
Num/StrConst/Var/Ctor, Sort.Int/String/Bool, Collector lifecycle.
v1.1.0 IR shape only: flat quantifiers, unified `operands`, no `sort`
on var/ctor, atomic uses `name`.

Cross-language conformance verified empirically:

- Canonicalizer fixture for `x > 0` produces JCS bytes byte-identical
  to the C++ peer and propertyHash blake3-512:c592f835...23a5 per
  spec (CanonicalizerConformanceTests).
- Kit's Gt(Var("x"), Num(0)) -> FormulaToValue -> JCS hashes to
  blake3-512:3e28aae8...95110, byte-identical to the Rust peer's
  provekit-ir-symbolic crate (IrKitConformanceTests).
- BLAKE3-512 of "hello" and empty input pinned against the Rust
  blake3 crate's 64-byte XOF output.

41/41 tests pass on `dotnet test`.

NuGet packages: Blake3 (1.x, with Hasher.Finalize 64-byte span for
true XOF), NSec.Cryptography (libsodium-backed Ed25519), xunit 2.x.
CBOR is hand-rolled (~70 LOC) to match the Rust/C++ peers exactly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The catalog cut at 87d552a moved the protocol forward but left the
consumer surfaces still pointing at v1.1.0:

  - implementations/rust/provekit-cli/src/protocol.rs:
    EXPECTED_CATALOG_CID was hardcoded as v1.1.0's 9d57c5e4...
    Updated to v1.2.0's 1e5cfee6...
  - implementations/rust/provekit-cli/assets/:
    catalog-signature-v1.2.0.json added, alongside v1.1.0.json.
    Embedded signature switched to v1.2.0.
  - implementations/rust/provekit-cli/src/main.rs:
    docstring referenced catalog-signature-v1.1.0.json. Updated.
  - tools/recompute-spec-cids/src/main.rs: comment + print line
    said "v1.1.0 catalog freeze tool". Updated to v1.2.0.
  - README.md: declared v1.1.0 as current canonical name. Updated
    to cite the v1.2.0 CID; v1.1.0 noted as still valid for
    anyone pinned.
  - docs/launch/bluepaper.md: self-contracts label changed from
    "(v1.1.0)" to "(stable; v1.1.0+)" since the contracts are
    byte-identical across the additive bump.

Verified with `provekit verify-protocol --signed`:

  expected         : blake3-512:1e5cfee6...17d0579f
  actual           : blake3-512:1e5cfee6...17d0579f
  attested CID     : blake3-512:1e5cfee6...17d0579f
  signer (pubkey)  : ed25519:IVL40Zt5HSRFMkLhXy6rbLfP+ntqXtMAl5YOBpiB2xI=
  CID match        : ok
  attested CID     : ok
  signer match     : ok
  signature        : ok
  status           : match

The v1.1.0 attestation remains on-disk at
.provekit/catalog-signatures/v1.1.0.json AND
implementations/rust/provekit-cli/assets/catalog-signature-v1.1.0.json
for anyone pinning to v1.1.0 explicitly via --signature-file.

Sir flagged the larger architectural point: this manual migration is
the workflow `provekit migrate` should automate. Captured as task
#178. The upgrade workflow IS itself a contract; pre = v(N) signed,
post = v(N+1) signed AND consumer surfaces migrated. ProvekIt should
self-apply to its own publishing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Captures the load-bearing architectural insight Sir surfaced
2026-04-30: the plugin lifter is the seam between the bounded
protocol substrate and the unbounded universe of things people want
to verify.

The substrate is bounded by design — canonical IR is finite, content
addressing is finite, the catalog is signed and frozen. The universe
of verifiable propositions is unbounded — software contracts, sensor
telemetry, scientific consensus, legal attestations, identity, FDA
forms, SOC2 controls, SystemVerilog assertions, banking validity,
supply-chain provenance, medical device interlocks. Anyone could add
a domain tomorrow.

Without the plugin lifter, ProvekIt is a tool: every authoring
surface lives in the Rust CLI; the matrix of N languages × M
annotation libraries × K test frameworks × L domain DSLs is
unbounded; whoever ships the CLI is the bottleneck for every
adoption decision; the "any verifiable proposition" claim is
aspirational.

With the plugin lifter, ProvekIt is a protocol: any third party
writes a lifter binary in any language, drops a manifest, configures
.provekit/config.toml; the Rust CLI dispatches over stdio JSON-RPC.
Adoption ceiling moves from "what the core team can ship" to "what's
content-addressable" — which is everything verifiable.

The LINQ lifter currently in flight is the existence proof: every
modern host language ships proposition syntax already (LINQ, filter,
any/all, comprehensions, Streams, iterator combinators, WHERE).
Lifters recognize that syntax. Plugin lifter dispatch makes the
recognition pluggable. The trojan horse goes deeper than annotation
libraries — it's every line of predicate syntax in every production
codebase.

Architectural punchline: ProvekIt is bounded substrate + unbounded
ecosystem; the plugin lifter is the seam; the substrate scales
because it doesn't grow; the ecosystem scales because the seam
dispatches; the protocol scales because the seam scales.

Linked from `protocol/specs/2026-04-30-lift-plugin-protocol.md` and
the bluepaper's §1.3 decoration section (forthcoming follow-up).
Standalone manifesto so it stands without requiring spec context.
Lives at `docs/launch/why-plugin-lifters.md` alongside the bluepaper.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ct / All / Any / Count / First / GroupBy / Sum)

Trojan-horse demonstration: every existing C# codebase using LINQ
becomes a ProvekIt source without annotation effort. Roslyn parses
+ semantic-models the source; recognised System.Linq calls translate
to v1.1.0 canonical IR-JSON contract mementoes.

- Provekit.Lift.Linq: standalone NuGet (Microsoft.CodeAnalysis.CSharp).
  No dep on Provekit.IR; emits IR-JSON directly with byte-equality
  to the kit's MarshalDeclarations encoder.
- Per-op translators for Where, Select, All, Any, Count, First, Single,
  Sum, GroupBy, OrderBy. Forward-only Where membership semantics; All
  emits implicit-membership forall (matches kit's must() form).
- Chain-DAG primitive: each chained LINQ statement records its
  receiver's binding name in InputBindings; the mint pipeline turns
  that into the inputCids edge. `users.Where(...).Where(...)` lifts to
  two contract mementoes wired adults <- users, voters <- adults.
- Query-syntax (`from x in xs where P(x) select x`) handled via
  ITranslatedQueryOperation walking; structurally equivalent to the
  method-syntax form.
- 9 xunit tests including a kit-authored byte-match conformance test
  (`SingleWhereIrJsonByteEqualsKitAuthoredEquivalent`).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 1, 2026 06:03
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of files (300). Try reducing the number of changed files and requesting a review from Copilot again.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 1, 2026

Important

Review skipped

Too many files!

This PR contains 293 files, which is 143 over the limit of 150.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 93534e99-02f7-42ca-ae09-ede4b962a65f

📥 Commits

Reviewing files that changed from the base of the PR and between 0d0a523 and cbf07ff.

⛔ Files ignored due to path filters (7)
  • .provekit/keys/foundation-v0.pub is excluded by !**/*.pub
  • examples/stage4-handshake-demo/go-validate-kit/go.sum is excluded by !**/*.sum
  • implementations/go/provekit-ir-symbolic/go.sum is excluded by !**/*.sum
  • implementations/go/provekit-lift-go-tests/go.sum is excluded by !**/*.sum
  • implementations/go/provekit-self-contracts/go.sum is excluded by !**/*.sum
  • implementations/rust/Cargo.lock is excluded by !**/*.lock
  • implementations/rust/provekit-cli/assets/foundation-v0.pub is excluded by !**/*.pub
📒 Files selected for processing (293)
  • .github/workflows/ci.yml
  • .gitignore
  • .provekit/catalog-signatures/v1.1.0.json
  • .provekit/catalog-signatures/v1.2.0.json
  • .provekit/keys/README.md
  • ARCHITECTURE.md
  • LANDING.md
  • Makefile
  • PITCH.md
  • PRODUCT.md
  • README.md
  • THESIS.md
  • bin/provekit-lift.cjs
  • docs/QUICKSTART.md
  • docs/getting-started.md
  • docs/launch/bluepaper.md
  • docs/launch/demo-script.md
  • docs/launch/showcase-results.md
  • docs/launch/whitepaper.md
  • docs/launch/why-plugin-lifters.md
  • docs/lift-adoption-paths.md
  • docs/per-language-status.md
  • docs/superpowers/specs/2026-04-30-provekit-lift-v0.md
  • docs/templates/provekit-example-workflow.yml
  • examples/agent-plugins/doubleledger-fixture/README.md
  • examples/agent-plugins/doubleledger-fixture/src/doubleledger.ts
  • examples/agent-plugins/echo-agent/echo_agent.py
  • examples/agent-plugins/echo-agent/manifest.toml
  • examples/ir-compiler-plugins/echo-compiler/echo_compiler.py
  • examples/ir-compiler-plugins/echo-compiler/manifest.toml
  • examples/multi-solver-demo/README.md
  • examples/multi-solver-demo/multi_solver_driver.rs
  • examples/stage4-handshake-demo/README.md
  • examples/stage4-handshake-demo/go-validate-kit/go.mod
  • examples/stage4-handshake-demo/go-validate-kit/main.go
  • examples/stage4-handshake-demo/run.sh
  • examples/stage4-handshake-demo/stage4_driver.rs
  • implementations/cpp/.provekit/config.toml
  • implementations/cpp/.provekit/lift/cpp-self-contracts/manifest.toml
  • implementations/cpp/provekit-ir-symbolic/example/cross_lang_consumer.cpp
  • implementations/cpp/provekit-ir-symbolic/example/parseInt_kit_proof.cpp
  • implementations/cpp/provekit-ir-symbolic/include/provekit/ir.hpp
  • implementations/cpp/provekit-self-contracts/mint_cpp_self_contracts.cpp
  • implementations/cpp/provekit/canonicalizer/BUILD.bazel
  • implementations/cpp/provekit/canonicalizer/canonicalizer_test.cpp
  • implementations/cpp/provekit/canonicalizer/hash.cpp
  • implementations/cpp/provekit/canonicalizer/hash.hpp
  • implementations/cpp/provekit/canonicalizer/hash.invariant.cpp
  • implementations/cpp/provekit/canonicalizer/jcs.invariant.cpp
  • implementations/cpp/provekit/canonicalizer/property_hash.cpp
  • implementations/cpp/provekit/canonicalizer/property_hash.hpp
  • implementations/cpp/provekit/canonicalizer/property_hash.invariant.cpp
  • implementations/cpp/provekit/canonicalizer/sha256.cpp
  • implementations/cpp/provekit/canonicalizer/sha256.hpp
  • implementations/cpp/provekit/claim-envelope/mint.cpp
  • implementations/cpp/provekit/claim-envelope/mint.hpp
  • implementations/cpp/provekit/claim-envelope/mint.invariant.cpp
  • implementations/cpp/provekit/claim-envelope/value_from_kit.cpp
  • implementations/cpp/provekit/proof-envelope/cbor.invariant.cpp
  • implementations/cpp/provekit/proof-envelope/cbor_decoder.hpp
  • implementations/cpp/provekit/proof-envelope/proof_envelope.cpp
  • implementations/cpp/provekit/proof-envelope/proof_envelope.hpp
  • implementations/cpp/provekit/proof-envelope/proof_envelope.invariant.cpp
  • implementations/cpp/provekit/proof-envelope/proof_envelope_test.cpp
  • implementations/cpp/provekit/proof-envelope/sign_ed25519.cpp
  • implementations/cpp/provekit/proof-envelope/sign_ed25519.hpp
  • implementations/cpp/provekit/proof-envelope/sign_ed25519.invariant.cpp
  • implementations/cpp/provekit/verifier/enumerate_callsites.cpp
  • implementations/cpp/provekit/verifier/enumerate_callsites.invariant.cpp
  • implementations/cpp/provekit/verifier/instantiate.cpp
  • implementations/cpp/provekit/verifier/instantiate.invariant.cpp
  • implementations/cpp/provekit/verifier/load_all_proofs.cpp
  • implementations/cpp/provekit/verifier/load_all_proofs.invariant.cpp
  • implementations/cpp/provekit/verifier/resolve_target.cpp
  • implementations/cpp/provekit/verifier/resolve_target.invariant.cpp
  • implementations/cpp/provekit/verifier/smt_emitter.cpp
  • implementations/csharp/.gitignore
  • implementations/csharp/Directory.Build.props
  • implementations/csharp/Provekit.Canonicalizer/Hash.cs
  • implementations/csharp/Provekit.Canonicalizer/Hash.invariant.cs
  • implementations/csharp/Provekit.Canonicalizer/Jcs.cs
  • implementations/csharp/Provekit.Canonicalizer/Jcs.invariant.cs
  • implementations/csharp/Provekit.Canonicalizer/Provekit.Canonicalizer.csproj
  • implementations/csharp/Provekit.Canonicalizer/Value.cs
  • implementations/csharp/Provekit.Canonicalizer/Value.invariant.cs
  • implementations/csharp/Provekit.ClaimEnvelope/Authoring.cs
  • implementations/csharp/Provekit.ClaimEnvelope/Authoring.invariant.cs
  • implementations/csharp/Provekit.ClaimEnvelope/Mint.cs
  • implementations/csharp/Provekit.ClaimEnvelope/Mint.invariant.cs
  • implementations/csharp/Provekit.ClaimEnvelope/Provekit.ClaimEnvelope.csproj
  • implementations/csharp/Provekit.IR/Collector.cs
  • implementations/csharp/Provekit.IR/Collector.invariant.cs
  • implementations/csharp/Provekit.IR/Formula.cs
  • implementations/csharp/Provekit.IR/Formula.invariant.cs
  • implementations/csharp/Provekit.IR/Predicates.cs
  • implementations/csharp/Provekit.IR/Predicates.invariant.cs
  • implementations/csharp/Provekit.IR/Provekit.IR.csproj
  • implementations/csharp/Provekit.IR/Quantifiers.cs
  • implementations/csharp/Provekit.IR/Quantifiers.invariant.cs
  • implementations/csharp/Provekit.IR/Serialize.cs
  • implementations/csharp/Provekit.IR/Serialize.invariant.cs
  • implementations/csharp/Provekit.IR/Sort.cs
  • implementations/csharp/Provekit.IR/Sort.invariant.cs
  • implementations/csharp/Provekit.IR/Term.cs
  • implementations/csharp/Provekit.IR/Term.invariant.cs
  • implementations/csharp/Provekit.Lift.Linq.Tests/DemoReportTests.cs
  • implementations/csharp/Provekit.Lift.Linq.Tests/LifterTests.cs
  • implementations/csharp/Provekit.Lift.Linq.Tests/Provekit.Lift.Linq.Tests.csproj
  • implementations/csharp/Provekit.Lift.Linq.Tests/SingleWhereTests.cs
  • implementations/csharp/Provekit.Lift.Linq.Tests/fixtures/all_predicate.cs
  • implementations/csharp/Provekit.Lift.Linq.Tests/fixtures/chained_where.cs
  • implementations/csharp/Provekit.Lift.Linq.Tests/fixtures/groupby_sum.cs
  • implementations/csharp/Provekit.Lift.Linq.Tests/fixtures/mixed_code.cs
  • implementations/csharp/Provekit.Lift.Linq.Tests/fixtures/query_syntax.cs
  • implementations/csharp/Provekit.Lift.Linq.Tests/fixtures/single_where.cs
  • implementations/csharp/Provekit.Lift.Linq/IRBuilder.cs
  • implementations/csharp/Provekit.Lift.Linq/LinqLifter.cs
  • implementations/csharp/Provekit.Lift.Linq/MethodTranslators.cs
  • implementations/csharp/Provekit.Lift.Linq/MintedMemento.cs
  • implementations/csharp/Provekit.Lift.Linq/PredicateTranslator.cs
  • implementations/csharp/Provekit.Lift.Linq/Provekit.Lift.Linq.csproj
  • implementations/csharp/Provekit.ProofEnvelope/Cbor.cs
  • implementations/csharp/Provekit.ProofEnvelope/Cbor.invariant.cs
  • implementations/csharp/Provekit.ProofEnvelope/Proof.cs
  • implementations/csharp/Provekit.ProofEnvelope/Proof.invariant.cs
  • implementations/csharp/Provekit.ProofEnvelope/Provekit.ProofEnvelope.csproj
  • implementations/csharp/Provekit.ProofEnvelope/Sign.cs
  • implementations/csharp/Provekit.ProofEnvelope/Sign.invariant.cs
  • implementations/csharp/Provekit.SelfContracts/Program.cs
  • implementations/csharp/Provekit.SelfContracts/Provekit.SelfContracts.csproj
  • implementations/csharp/Provekit.Tests/CanonicalizerConformanceTests.cs
  • implementations/csharp/Provekit.Tests/ClaimEnvelopeTests.cs
  • implementations/csharp/Provekit.Tests/IrKitConformanceTests.cs
  • implementations/csharp/Provekit.Tests/ProofEnvelopeTests.cs
  • implementations/csharp/Provekit.Tests/Provekit.Tests.csproj
  • implementations/csharp/Provekit.Tests/SelfContractsDeterminismTests.cs
  • implementations/csharp/Provekit.sln
  • implementations/csharp/README.md
  • implementations/go/.provekit/config.toml
  • implementations/go/.provekit/lift/go-self-contracts/manifest.toml
  • implementations/go/provekit-ir-symbolic/canonicalizer/encoder_test.go
  • implementations/go/provekit-ir-symbolic/canonicalizer/hasher.go
  • implementations/go/provekit-ir-symbolic/claim_envelope/bridge_minter.go
  • implementations/go/provekit-ir-symbolic/claim_envelope/contract_minter.go
  • implementations/go/provekit-ir-symbolic/claim_envelope/envelope.go
  • implementations/go/provekit-ir-symbolic/claim_envelope/implication_minter.go
  • implementations/go/provekit-ir-symbolic/claim_envelope/property_minter.go
  • implementations/go/provekit-ir-symbolic/cmd/go-kit-publish/main.go
  • implementations/go/provekit-ir-symbolic/go.mod
  • implementations/go/provekit-ir-symbolic/ir/canonical_form_test.go
  • implementations/go/provekit-ir-symbolic/ir/canonicalize.go
  • implementations/go/provekit-ir-symbolic/ir/primitives.go
  • implementations/go/provekit-ir-symbolic/ir/primitives_test.go
  • implementations/go/provekit-ir-symbolic/ir/property.go
  • implementations/go/provekit-ir-symbolic/ir/property_test.go
  • implementations/go/provekit-ir-symbolic/ir/quantifiers.go
  • implementations/go/provekit-ir-symbolic/ir/quantifiers_test.go
  • implementations/go/provekit-ir-symbolic/ir/types.go
  • implementations/go/provekit-ir-symbolic/ir/types_test.go
  • implementations/go/provekit-ir-symbolic/proof_envelope/builder.go
  • implementations/go/provekit-ir-symbolic/verifier/cross_lang_demo_test.go
  • implementations/go/provekit-ir-symbolic/verifier/enumerate_callsites.go
  • implementations/go/provekit-ir-symbolic/verifier/instantiate.go
  • implementations/go/provekit-ir-symbolic/verifier/load_all_proofs.go
  • implementations/go/provekit-ir-symbolic/verifier/resolve_target.go
  • implementations/go/provekit-ir-symbolic/verifier/smt_emitter.go
  • implementations/go/provekit-ir-symbolic/verifier/types.go
  • implementations/go/provekit-lift-go-tests/README.md
  • implementations/go/provekit-lift-go-tests/fixtures/layer2_sample.go.txt
  • implementations/go/provekit-lift-go-tests/go.mod
  • implementations/go/provekit-lift-go-tests/integration_test.go
  • implementations/go/provekit-lift-go-tests/layer2.go
  • implementations/go/provekit-lift-go-tests/layer2_test.go
  • implementations/go/provekit-lift-go-tests/leaf.go
  • implementations/go/provekit-lift-go-tests/marshal.go
  • implementations/go/provekit-lift-go-tests/output.go
  • implementations/go/provekit-lift-go-tests/subst.go
  • implementations/go/provekit-self-contracts/cmd/mint-go-self-contracts/main.go
  • implementations/go/provekit-self-contracts/cmd/mint-go-self-contracts/rpc.go
  • implementations/go/provekit-self-contracts/go.mod
  • implementations/go/provekit-self-contracts/slabs/canonicalizer.go
  • implementations/go/provekit-self-contracts/slabs/claim_envelope.go
  • implementations/go/provekit-self-contracts/slabs/ir.go
  • implementations/go/provekit-self-contracts/slabs/proof_envelope.go
  • implementations/go/provekit-self-contracts/slabs/slabs.go
  • implementations/go/provekit-self-contracts/slabs/verifier.go
  • implementations/python/provekit-lift-py-tests/README.md
  • implementations/python/provekit-lift-py-tests/pyproject.toml
  • implementations/python/provekit-lift-py-tests/src/provekit_lift_py_tests/__init__.py
  • implementations/python/provekit-lift-py-tests/src/provekit_lift_py_tests/canonicalizer.py
  • implementations/python/provekit-lift-py-tests/src/provekit_lift_py_tests/ir.py
  • implementations/python/provekit-lift-py-tests/src/provekit_lift_py_tests/layer2.py
  • implementations/python/provekit-lift-py-tests/tests/conftest.py
  • implementations/python/provekit-lift-py-tests/tests/fixtures/layer2_sample.py
  • implementations/python/provekit-lift-py-tests/tests/test_canonicalizer.py
  • implementations/python/provekit-lift-py-tests/tests/test_integration.py
  • implementations/python/provekit-lift-py-tests/tests/test_ir_conformance.py
  • implementations/python/provekit-lift-py-tests/tests/test_layer2.py
  • implementations/rust/.gitignore
  • implementations/rust/.provekit/config.toml
  • implementations/rust/.provekit/lift/rust-self-contracts/manifest.toml
  • implementations/rust/Cargo.toml
  • implementations/rust/examples/build_script_demo/Cargo.toml
  • implementations/rust/examples/build_script_demo/README.md
  • implementations/rust/examples/build_script_demo/build.rs
  • implementations/rust/examples/build_script_demo/src/lib.rs
  • implementations/rust/examples/build_script_demo/src/lift_examples.rs
  • implementations/rust/examples/cross_lang_consume.rs
  • implementations/rust/examples/layer2_py_probe.rs
  • implementations/rust/examples/macro_smoke.rs
  • implementations/rust/examples/parseInt_publish.rs
  • implementations/rust/examples/rust_round_trip.rs
  • implementations/rust/examples/scan_self.rs
  • implementations/rust/provekit-agent-claude-code/Cargo.toml
  • implementations/rust/provekit-agent-claude-code/src/lib.rs
  • implementations/rust/provekit-agent-claude-code/src/subprocess.rs
  • implementations/rust/provekit-agent-openai/Cargo.toml
  • implementations/rust/provekit-agent-openai/src/lib.rs
  • implementations/rust/provekit-agent/Cargo.toml
  • implementations/rust/provekit-agent/src/lib.rs
  • implementations/rust/provekit-agent/src/loop_fix.rs
  • implementations/rust/provekit-agent/src/loop_lift.rs
  • implementations/rust/provekit-agent/src/loop_must.rs
  • implementations/rust/provekit-agent/src/stub.rs
  • implementations/rust/provekit-agent/tests/doubleledger.rs
  • implementations/rust/provekit-build/Cargo.toml
  • implementations/rust/provekit-build/README.md
  • implementations/rust/provekit-build/SPEC.md
  • implementations/rust/provekit-build/src/lib.rs
  • implementations/rust/provekit-build/src/lift_pass.rs
  • implementations/rust/provekit-build/src/source_walk.rs
  • implementations/rust/provekit-build/tests/integration.rs
  • implementations/rust/provekit-canonicalizer/Cargo.toml
  • implementations/rust/provekit-canonicalizer/src/hash.invariant.rs
  • implementations/rust/provekit-canonicalizer/src/hash.rs
  • implementations/rust/provekit-canonicalizer/src/jcs.invariant.rs
  • implementations/rust/provekit-canonicalizer/src/jcs.rs
  • implementations/rust/provekit-canonicalizer/src/lib.rs
  • implementations/rust/provekit-canonicalizer/src/value.rs
  • implementations/rust/provekit-canonicalizer/tests/cid_invariants.rs
  • implementations/rust/provekit-canonicalizer/tests/hash_known_answer.rs
  • implementations/rust/provekit-canonicalizer/tests/jcs_extended.rs
  • implementations/rust/provekit-claim-envelope/Cargo.toml
  • implementations/rust/provekit-claim-envelope/src/lib.invariant.rs
  • implementations/rust/provekit-claim-envelope/src/lib.rs
  • implementations/rust/provekit-claim-envelope/tests/mint_bridge_implication.rs
  • implementations/rust/provekit-claim-envelope/tests/mint_contract.rs
  • implementations/rust/provekit-cli/Cargo.toml
  • implementations/rust/provekit-cli/assets/catalog-signature-v1.1.0.json
  • implementations/rust/provekit-cli/assets/catalog-signature-v1.2.0.json
  • implementations/rust/provekit-cli/assets/protocol-catalog.json
  • implementations/rust/provekit-cli/prompts/fix/default.md
  • implementations/rust/provekit-cli/prompts/lift/default.md
  • implementations/rust/provekit-cli/prompts/must/default.md
  • implementations/rust/provekit-cli/src/cmd_agent.rs
  • implementations/rust/provekit-cli/src/cmd_ask.rs
  • implementations/rust/provekit-cli/src/cmd_dump.rs
  • implementations/rust/provekit-cli/src/cmd_fix.rs
  • implementations/rust/provekit-cli/src/cmd_hash.rs
  • implementations/rust/provekit-cli/src/cmd_implicate.rs
  • implementations/rust/provekit-cli/src/cmd_init.rs
  • implementations/rust/provekit-cli/src/cmd_lift.rs
  • implementations/rust/provekit-cli/src/cmd_mint.rs
  • implementations/rust/provekit-cli/src/cmd_must.rs
  • implementations/rust/provekit-cli/src/cmd_prove.rs
  • implementations/rust/provekit-cli/src/cmd_search.rs
  • implementations/rust/provekit-cli/src/cmd_verify_protocol.rs
  • implementations/rust/provekit-cli/src/cmd_version.rs
  • implementations/rust/provekit-cli/src/main.rs
  • implementations/rust/provekit-cli/src/project_config.rs
  • implementations/rust/provekit-cli/src/prompts.rs
  • implementations/rust/provekit-cli/src/protocol.rs
  • implementations/rust/provekit-cli/src/report_fmt.rs
  • implementations/rust/provekit-ir-compiler-smt-lib/Cargo.toml
  • implementations/rust/provekit-ir-compiler-smt-lib/src/bin/provekit_ir_smt_lib.rs
  • implementations/rust/provekit-ir-compiler-smt-lib/src/lib.rs
  • implementations/rust/provekit-ir-compiler-smt-lib/tests/byte_for_byte.rs
  • implementations/rust/provekit-ir-compiler-smt-lib/tests/emitter.rs
  • implementations/rust/provekit-ir-compiler-smt-lib/tests/subprocess.rs
  • implementations/rust/provekit-ir-compiler-stub/Cargo.toml
  • implementations/rust/provekit-ir-compiler-stub/src/lib.rs
  • implementations/rust/provekit-ir-compiler-stub/tests/stub.rs
  • implementations/rust/provekit-ir-compiler/Cargo.toml
  • implementations/rust/provekit-ir-compiler/src/error.rs
  • implementations/rust/provekit-ir-compiler/src/lib.rs
  • implementations/rust/provekit-ir-compiler/src/manifest.rs
  • implementations/rust/provekit-ir-compiler/src/registry.rs
  • implementations/rust/provekit-ir-compiler/src/subprocess.rs
  • implementations/rust/provekit-ir-compiler/tests/error_codes.rs
  • implementations/rust/provekit-ir-compiler/tests/manifest.rs
  • implementations/rust/provekit-ir-compiler/tests/registry.rs
  • implementations/rust/provekit-ir-symbolic/Cargo.toml
  • implementations/rust/provekit-ir-symbolic/README.md

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/protocol-v1-contract

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

…hestrator, .proof)

The C# kit + lib (Phase 1+2) ships with a working dogfood. 15 sidecar
.invariant.cs files declare 70 contracts about the C# library's own public
surface (Hash, Jcs, Value, Sort, Term, Formula, Predicates, Quantifiers,
Collector, Serialize, Authoring, Mint, Cbor, Sign, Proof). The
Provekit.SelfContracts orchestrator mints each as a signed memento under the
foundation key (test seed [0x42; 32]) and bundles into a .proof whose
filename IS its catalog CID.

Determinism check passes: byte-identical CIDs across two runs.

  catalog CID: blake3-512:45d7cdbd0d5bfba5a1ee9e8386eb4d7dc1eab0882105753504a1f5c06de6f9fc4bd7038f56c7fcea693b152e2ab83de40ca4964a920816142ea43d5b9076415c

The four lib csprojs use <Compile Remove="**/*.invariant.cs" /> so sidecars
don't pollute the lib assemblies; Provekit.SelfContracts.csproj re-imports
them. Provekit.Tests + IrKitConformanceTests share xUnit collection
"CollectorSerial" with the new SelfContractsDeterminismTests so the
process-global Collector singleton is never raced. Test count: 41 -> 42.

C# is the fifth peer (alongside Rust, Go, C++, TypeScript). bluepaper
Appendix A.1 updated to cite the C# CID and bumps the closing line to
"Five peers, five CIDs, one protocol."

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ebd9dc63ba

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +267 to +268
.surface_for("must")
.or_else(|| user_cfg.surface_for("must"))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Resolve mint surface from lift config scope

provekit mint currently reads [authoring.must]/must defaults instead of the lift scope, so repositories that intentionally separate surfaces (for example, must using a contracts surface while lift points to a plugin surface) will dispatch the wrong plugin or fail manifest lookup. This regresses multi-surface setups because mint is the lift-plugin dispatcher and should honor the same lift-scoped surface resolution used by the lift path.

Useful? React with 👍 / 👎.

TSavo and others added 5 commits April 30, 2026 23:15
The C++ orchestrator's `signer_cid` was a hardcoded placeholder copied
from the parseInt_kit_proof example. It existed for weeks. The .proof's
`signer_cid` did not actually correspond to the foundation key bytes;
a verifier walking signer_cid -> key memento got gibberish. Sir caught
it.

Root cause Sir surfaced rhetorically: "why is that not an invariant?
f(x(a))=a?" — the placeholder bug is exactly the class of bug a
content-addressed contract memento is supposed to refute. We had no
contract enforcing the round-trip; the placeholder drifted undetected.

This commit:

1. Adds `ed25519_pubkey_from_seed(seed)` and
   `ed25519_pubkey_string_from_seed(seed)` to the C++ proof-envelope
   library. OpenSSL EVP_PKEY_get_raw_public_key under the hood;
   produces byte-identical pubkeys to Rust ed25519-dalek, NSec
   (C# / .NET), and node:crypto.

2. Replaces the hardcoded placeholder in mint_cpp_self_contracts.cpp
   with the protocol-correct derivation:
       signer_cid = compute_cid(ed25519_pubkey_string_from_seed(seed))
   The .proof's signer_cid now actually identifies the foundation v0
   key.

3. Adds the two missing f(x(a))=a invariants:
   - sign_ed25519.invariant.cpp:
       verify(sign(msg, seed), pubkey_from_seed(seed), msg) = true
     The canonical round-trip identity. Z3 cannot discharge it
     (cryptographic semantics outside FOL), but the contract IS
     content-addressed and IS available for any verifier that wants
     to check it.
   - proof_envelope.invariant.cpp:
       proof_envelope_signer_cid_for_seed(seed) =
           compute_cid(pubkey_string_from_seed(seed))
     The construction invariant. With this contract minted, a future
     placeholder drift becomes refutable at mint-time.

C++ self-contracts CID changes accordingly:
  was: blake3-512:52bc62f7...cd323ae0  (40 contracts; placeholder signer_cid)
  now: blake3-512:9335e637...17e62842  (42 contracts; real signer_cid)

Determinism check passes for the new value. The four other peer CIDs
(Rust, Go, TS, C#) are unchanged because their orchestrators were
already protocol-correct (the C# Phase 3 agent flagged this exact
issue when noting "signer_cid derived as
Blake3-512(Ed25519PubkeyString(seed)), mirroring the Rust peer (the
protocol-correct pattern), not the C++ hardcoded placeholder.").

Bluepaper Appendix A.1 updated to the new C++ CID.

The meta-demonstration: the framework's own publishing pipeline
shipped a placeholder for weeks. The framework couldn't catch it
for itself because the invariant didn't exist. Now it does. This is
exactly the bug class invariants are designed to refute, and the
mechanism is now live in C++ self-contracts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sir directive: warnings are errors across all peer builds.

C++:
  - Added -Werror to tools/build-cpp-self-contracts.sh
  - Removed unused emit_pair_with_raw_value() in proof_envelope.cpp
    (defined but never called; per the project's content-address rule,
    if you can't address it, delete it)
  - Build is clean with -Werror; CID unchanged at
    9335e637...17e62842 (deleted function was unreferenced; no output
    bytes change)

Rust:
  - Workspace now builds clean with RUSTFLAGS=-Dwarnings
  - Removed dead `i += 1` after break in showcase bench.rs:683
  - #[allow(dead_code)] on three intentional-but-unwired API surfaces:
    - provekit-cli/project_config.rs: KNOWN_SURFACES, KNOWN_AGENTS,
      KNOWN_SOLVERS (menu data for `provekit init` interactive flow,
      not yet wired)
    - provekit-cli/project_config.rs: merged_for_command (public API
      consumed by init flow, not yet wired)
    - provekit-showcase/bench.rs: ImpRecord.cid (captured for
      diagnostic; bench currently uses hashes only)
    - provekit-showcase/bench.rs: read_array_head (symmetric to
      read_map_head; bench data exercises maps but pair stays)

The two remaining `cargo:warning=` lines from build-script-demo are
build-script diagnostic output, not rustc warnings; -Dwarnings does
not escalate them. They stay (the deliberate_violation is the
fixture demonstrating violation detection — that's the feature).

Go: not applicable (compiler emits no warnings; nothing to escalate).
C#, TS: TreatWarningsAsErrors / lint-strict policy todo.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sir directive: warnings are errors across the polyglot.

For C# this lands as a single Directory.Build.props at the solution
root that MSBuild auto-applies to every csproj under
implementations/csharp/. Mirrors the Rust workspace's RUSTFLAGS=-Dwarnings
and the C++ build script's -Werror.

All 8 csproj files inherit:
  Provekit.Canonicalizer
  Provekit.IR
  Provekit.ProofEnvelope
  Provekit.ClaimEnvelope
  Provekit.SelfContracts
  Provekit.Tests
  Provekit.Lift.Linq
  Provekit.Lift.Linq.Tests

dotnet build Provekit.sln: 0 warnings, 0 errors.
dotnet build Provekit.Lift.Linq.Tests: 0 warnings, 0 errors.

Per-csproj <TreatWarningsAsErrors> still wins for any project that
needs to override (none today).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ection

Adds the missing top-level orchestration for the 5-language polyglot:

1. .github/workflows/ci.yml — single ubuntu-latest job that installs
   Rust stable, Go 1.22, .NET 10 preview, Node 22 + pnpm 10, Python
   3.12, plus libssl-dev + nlohmann-json3-dev system deps. BLAKE3 is
   vendored under tools/blake3-vendored/ (portable C, no system
   package), so the C++ build is hermetic. The job runs `make
   conformance` (catalog v1.2.0 hash equality, signed protocol
   verify, all 5 peer self-contract CIDs equal pinned values) and
   `make test-all` (every language-native suite). Concurrency-cancel
   on superseded runs.

2. Makefile — top-level orchestrator. `make ci` is the contract; if
   it's green, the protocol is byte-deterministic across all 5 peers
   and every native test suite passes. Per-language build/test
   targets (build-rust, build-cpp, mint-rust, mint-go, ...) shell out
   to each language's native tool. Pinned CIDs at the top of the
   file are the source of truth; CI greps for them.

3. README.md — Building section with per-language quickstart, the
   `make ci` one-liner, brew/apt dep table, BLAKE3-vendoring note,
   and the documented-broken bin/provekit*.cjs launchers (vitest path
   is the working invocation; CI uses it).

4. tools/blake3-vendored/ — BLAKE3 1.8.5 portable C sources (Apache-
   2.0). Compiled with -DBLAKE3_NO_AVX2/AVX512/SSE2/SSE41 and
   -DBLAKE3_USE_NEON=0 so the dispatcher selects the portable path
   unconditionally. Builds clean on x86_64, arm64, anywhere clang
   runs. Removes the brew/apt libblake3-dev dependency entirely.

5. .gitignore — implementations/{rust,cpp}/target/, csharp bin/obj,
   go binaries, blake3-512:*.proof artifacts.

6. docs/templates/provekit-example-workflow.yml — moved out of
   .github/workflows/ so it doesn't trigger as a real workflow on
   every push (it's a downstream-consumer template, not a ProvekIt
   self-CI).

Pinned CIDs verified locally (macOS, commit 84b064f tree):
  catalog: 1e5cfee6...717d0579f
  rust:    b692f43a...478434dfe
  go:      906fa4f3...23487ac3
  cpp:     9335e637...17e62842
  ts:      44933993...d4904d44
  csharp:  45d7cdbd...9076415c

The C++ CID changed from 52bc62f7 -> 9335e637 in commit 5020741
(real signer_cid derivation + round-trip invariants), then stayed
9335e637 through 24ca3a2 (-Werror cleanup; deleted unused function
was unreferenced, no output bytes change). The ci.yml + Makefile
pin the post-fix value 9335e637.

`make conformance` passes locally with all 5 CIDs matching pinned
values and the catalog v1.2.0 + signed protocol verifying.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI exposed a macOS-vs-Linux header difference. The file uses
std::shared_ptr<CborValue>; macOS Apple Clang/libc++ pulls shared_ptr
in transitively from <variant> or <map>, but Ubuntu GCC 14/libstdc++
doesn't. Local builds passed; CI failed.

Adding <memory> explicitly. The include was actually present in my
working copy locally (added at some point by an editor or earlier
session) but had never been committed. CI hit HEAD state, not local
state.

Standard practice: include every header you use directly, even if
some compiler+stdlib pair makes it transitively available. The
protocol's promise of byte-identical output across implementations
needs to extend to byte-identical builds across platforms.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@TSavo TSavo merged commit 96178a7 into main May 1, 2026
1 of 2 checks passed
TSavo added a commit that referenced this pull request May 5, 2026
…370, #384)

Fixes all P1/major/minor issues flagged by Copilot + CodeRabbit:

walk.rs:
- Bug #1: nested-block let-bindings now substituted at nested callsites;
  `CallsiteHit` carries `preceding_inner_stmts` (innermost-first) populated
  during the forward traversal; backward walk applies them before the outer-
  body statements.
- Bug #3: actual-arg lift now delegates to `crate::lift::lift_expr_to_term`
  (semantic, structure-stable) instead of the old token-string fallback;
  binary-expr actuals produce Ctor(op, ...) not `<expr:40 + 2>` tokens.
- Bug #4: arity mismatch now logs a warning and skips the callsite rather
  than silently zip-truncating. New test asserts the skip.
- Bug #11: stale MVP-scope comment updated (if-strengthening is live).

lift.rs:
- Bug #5: `debug_assert!` removed from the macro match arm; it is compiled
  out in release builds and must not contribute to contracts.
- Bug #6: `lift_function_postcondition` now scans for later `let` bindings
  that shadow an entry assertion's free variables and drops unsound asserts.
  Narrowed to LATER rebindings only (a preceding `let y = x; assert!(y ≥ 5)`
  is sound).
- Bug #7: explicit `return expr;` tails now derive `result = <lifted expr>`
  postcondition (previously only trailing-no-semicolon expressions matched).

canonical.rs:
- Bug #8: non-i64 JSON Numbers (floats, large u64) are now encoded as a
  tagged object `{"__provekit_non_i64_number__": "42.5"}` instead of a plain
  string, preventing numeric constants and string literals of the same text
  from sharing a CID.

walk_emit.rs / walk_demo.rs:
- Bug #9: `all_param_names` now synthesizes stable placeholder names
  (`__self`, `__arg{i}`) for non-Ident patterns so formal arity stays
  aligned with actual-argument count.
- Bug #10: NaN% division guarded with `if second_run_total > 0`.

11 new unit tests added (156 lib tests total, up from 145; 12 integration
tests all still passing).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TSavo added a commit that referenced this pull request May 5, 2026
…366) (#387)

* fix(baselines): resolve P1 review concerns from #293/#326 inline

Closes #366 partial (generator + BridgeV14 kit work).

P1 concerns addressed:

#1 Critical (CodeRabbit) — apply_builtin dead ctor0 branch: removed the
unreachable `a == 0` guard introduced by `let a = if arity == 0 { 1 }`.
Now arity=0 (serde default) correctly means unary; the ctor0 path is gone.

#5 Major (Copilot) — gte double-wrap: the old gte arm applied
ctor1(left, ctor1(sig, s)) in both branches of a dead if/else.
Fixed to ctor1(left, s) — the property applied directly to the input
variable, not to the builtin result.

#9 + #11 (CodeRabbit Major) — rust in lang list causes duplicate
rust-std-baseline-v1: removed "rust" from the hard-coded langs array.
rust is already minted by provekit-baseline-rust-std (#292). rust.toml
kept as reference but is not read by this binary.

#13 (Copilot) — silent skip on missing config changed to panic: a
missing config is always a repo error, not a runtime skip.

#14 (Copilot) — unknown predicate kind silently skipped changed to
panic: unknown kinds indicate a stale config or missing handler, not
a soft warning.

#15 (Copilot Major) — missing gte.args.left defaulting to signature
changed to panic: malformed configs must not silently produce
wrong-but-passing baselines.

#19 (CodeRabbit Major) — PHP Minter.php mintBridgeV14 now validates
and normalizes the tagged-union target before signing. Extra keys are
stripped; missing kind or cid fails with InvalidArgumentException.

#20 (CodeRabbit Critical) — Python bridge_v14.py missing `import base64`
added. The base64 module is used at line 101 but was not imported.

BridgeV14 kit work (from #326, unique to that branch):
- C kit: bridge_v14.c + self_contracts.h header
- PHP kit: Minter.php (+ validation fix above)
- Per-kit BridgeV14 in cpp, csharp, go, python, ruby, swift, ts, zig

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(baselines): regenerate 11 per-kit std baseline catalogs

Baselines minted by `mint-all-baselines` against the corrected
orchestrator (P1 fixes from previous commit). Rust excluded from this
binary — handled by provekit-baseline-rust-std.

11 kits minted: c, cpp, csharp, go, java, php, python, ruby, swift,
typescript, zig.

CIDs:
  c          blake3-512:6a30a452...
  cpp        blake3-512:64fc0845...
  csharp     blake3-512:e221722e...
  go         blake3-512:efa64a6c...
  java       blake3-512:22a41119...
  php        blake3-512:93028afd...
  python     blake3-512:224e0cbe...
  ruby       blake3-512:1c52edaf...
  swift      blake3-512:313ab922...
  typescript blake3-512:9da2bdff...
  zig        blake3-512:5f7e1f73...

These are byte-deterministic: same seed (FOUNDATION_V0_SEED = [0x42; 32]),
same configs, same produced_at timestamp. Regeneration on any platform
with the corrected binary must produce identical CIDs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(baseline-std): silence dead-code warnings + document rust.toml exclusion

- Rename ctorN -> ctor_n (snake_case, fixes non_snake_case warning)
- Add #[allow(dead_code)] to ctor0/ctor2 with explanatory comment; they are
  scaffolding for future N-ary predicate kinds, not bugs
- Add comment to configs/rust.toml explaining it is intentionally excluded from
  the mint_all_baselines langs list — Rust baselines are owned by
  provekit-baseline-rust-std (#292) to avoid duplicate rust-std-baseline-v1

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
TSavo added a commit that referenced this pull request May 28, 2026
…demo builds + tests green end-to-end

Adopt resolution path (b) from issue #1575: rename sql_query_row_string
to sql_query_row<T, P, F> matching the rusqlite shim's canonical
4-param mapper form. Callers in insert_event (rowid query) and
compose_report (event_type, user, payload_text queries) pass
|row| row.get(0) closures.

Verified green:
  cargo build  --manifest-path examples/voltron-demo/Cargo.toml: ok
  cargo test   --manifest-path examples/voltron-demo/Cargo.toml: 11/11 pass
    - tests/ingest_test.rs: 5/5
    - tests/persist_test.rs: 3/3
    - tests/voltron_e2e_test.rs: 3/3 (including full_spine_round_trip_succeeds,
      the cross-vendor end-to-end witness)
  cargo run    voltron-demo: 'voltron round-trip: rowid=1 user=alice type=signup
                              report="{\"age\":30}"'

The bin output shows the cross-vendor seam closed: serde_json::to_string
of the payload feeds rusqlite's INSERT, then rusqlite's SELECT feeds
serde_json::from_str back to a Value, and the spine prints age=30
round-tripped through both vendors.

Also adds .provekit/config.toml declaring rust-sugar + rust-contracts
lift surfaces. provekit mint dispatches but warns the lifter binary at
implementations/rust/target/debug/provekit-walk-rpc resolves against
the demo's project root, not the workspace root. Mint+prove polish
deferred (small follow-up commit pointing the manifest at the workspace
binary or teaching the plugin loader to walk up the monorepo).

Updates RESULT.md with the verified-green section + closes Gap #5 inline.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
TSavo added a commit that referenced this pull request Jun 5, 2026
Guard shapes #2-#5 (index-bounds, empty-container, divide-by-zero, key-access)
proved on BOTH numpy and pandas -- 8 cells, each a real library idiom with a
discrimination mutant. Zero code changes.

These are RUNTIME faults (IndexError / ValueError / KeyError / silent inf), not
logical contradictions, so the consistency (z3) axis is blind to them: `a[5] ==
0` is a consistent proposition. Only running the code catches the bug, so this
example registers ONLY the pytest-witness seat -- the axis that proves
correctness by execution. It re-runs each case under real numpy/pandas: the
guarded `_ok` case is witnessed (discharged); the `_bad` case breaches the guard
so the run raises (or, for divide-by-zero, the finiteness assertion fails) and
the witness is refused.

Divide-by-zero is the sharp cell: numpy/pandas return silent `inf` rather than
raising; provekit catches it because `_ok` asserts isfinite and `_bad` fails it.

The witness is per-file (runs `pytest <file>`), so `_ok`/`_bad` are separate
files -- 8 cells x 2 = 16. run.sh checks the verdict PER FILE (every `_ok`
discharged, every `_bad` refused), so a swapped or missing verdict fails the
gate. Verified green: 8 discharged, 8 refused.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
TSavo added a commit that referenced this pull request Jun 6, 2026
)

* Add the pandas.testing lift seat (sibling of numpy.testing)

The next package up the ladder from numpy is pandas, and per the federation
design it is just another lifter package: provekit-lift-py-pandas-testing, the
exact sibling of provekit-lift-py-numpy-testing, reusing the pytest seat's
term/formula machinery so equivalent claims federate.

The pandas EXACT/APPROXIMATE discipline is SHARPER than numpy's because pandas
makes approximate the DEFAULT: assert_frame_equal / assert_series_equal compare
floats with a tolerance unless check_exact is pinned. (Confirmed against pandas
3.0.3: check_exact's default is the <no_default> sentinel, resolved at runtime
by dtype.) So lifting an un-pinned frame assertion as `=` would FALSE-PASS,
claiming exact equality pandas never checked. Version-independently, the seat
lifts a frame/series/index/extension-array assertion as `=` ONLY when
check_exact=True is explicitly pinned AND no relation-altering keyword
(check_like, rtol/atol, check_dtype, ...) is present; everything else is a LOUD
REFUSE.

The whole pandas._testing (tm) 22-name vocabulary is recognised so nothing real
is silently skipped -- the trichotomy: express the exact, loudly refuse the
rest. assert_equal is deliberately excluded (cross-library generic name owned by
the numpy/generic seat).

Honest scope: a frame/series equality is opaque-EUF on both sides, so z3 cannot
manufacture a contradiction between two opaque DataFrame constructors -- same as
numpy array equality. The CONSISTENCY teeth for pandas come from SCALAR
assertions, which the pytest bare-assert seat already lifts (and this seat lifts
when they ride a claimed pandas.testing test). This seat's load-bearing job is
sound loud-refusal of approximate frame comparison plus callsite keying of the
pandas op under test for the witness axis.

17 unit tests: positive lifts (check_exact pinned), the approximate-by-default
refusal, relation-altering-kwarg refusals, mutation/control-flow/side-effect
refusals, SSA independence, broad-tm claim+refuse, and assert_equal non-claim.
Wired into the Makefile test-python loop.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add pandas-showcase: the two-axis correctness claim on pandas

Stands up pandas next to numpy, proving correctness with zero code changes.
Differs from numpy-showcase only by swapping in the pandas.testing seat and
pointing the witness venv at pandas -- the next package is just another lifter
package, as designed.

Three lift seats over the project: the plain pytest CONSISTENCY seat (scalar
assertions, where z3's teeth are), the pandas.testing seat (frame assertions;
approximate-by-default refused unless check_exact pinned), and pytest-witness
(runs the tests under real pandas). The project deliberately contains a buggy
(self-contradictory) test, so the showcase proves the CORRECT pandas code AND
catches the contradiction -- refused BOTH ways:
  consistency: Series.sum() == 6 consistent (discharged); == 6 AND == 7 UNSAT
               (refused).
  witness    : the good tests reproduce (discharged); the contradictory run is
               'failed' (refused).

run.sh is self-checking: it asserts provekit produces exactly that verdict and
exits non-zero otherwise. Verified green (4 discharged, 2 refused).

Scope: demonstrates lift + prove (consistency + witness by recompute). The
stricter signed-receipt verify path re-resolves each witness body, which for the
pytest-witness kit needs a packaged witness body or full re-run metadata
forwarded by the verifier -- a follow-up increment, noted in the README.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add python-guard-shapes: the 2x4 runtime-guard matrix (numpy + pandas)

Guard shapes #2-#5 (index-bounds, empty-container, divide-by-zero, key-access)
proved on BOTH numpy and pandas -- 8 cells, each a real library idiom with a
discrimination mutant. Zero code changes.

These are RUNTIME faults (IndexError / ValueError / KeyError / silent inf), not
logical contradictions, so the consistency (z3) axis is blind to them: `a[5] ==
0` is a consistent proposition. Only running the code catches the bug, so this
example registers ONLY the pytest-witness seat -- the axis that proves
correctness by execution. It re-runs each case under real numpy/pandas: the
guarded `_ok` case is witnessed (discharged); the `_bad` case breaches the guard
so the run raises (or, for divide-by-zero, the finiteness assertion fails) and
the witness is refused.

Divide-by-zero is the sharp cell: numpy/pandas return silent `inf` rather than
raising; provekit catches it because `_ok` asserts isfinite and `_bad` fails it.

The witness is per-file (runs `pytest <file>`), so `_ok`/`_bad` are separate
files -- 8 cells x 2 = 16. run.sh checks the verdict PER FILE (every `_ok`
discharged, every `_bad` refused), so a swapped or missing verdict fails the
gate. Verified green: 8 discharged, 8 refused.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Fix verify witness re-resolution for all-tests projects (empty code_files)

The signed-receipt verify path refused good witnesses with "no package file and
not re-runnable" on projects that are all tests (numpy/pandas showcases). Root
cause: the resolve_witness RECOMPUTE guard tested `memento.get("code_files")`
for truthiness, but an all-tests project pins an EMPTY code_files (the code under
test is the installed library, not a local file), and `[]` is falsy in Python.
So a trivially re-runnable witness was declared not re-runnable.

Fix is one line: gate on `code_files is not None` (present), not truthiness. The
empty list is part of the pinned witness body and reconstructs correctly, so the
tamper check at line 150 still holds -- this stays sound.

After the fix, verify re-resolves the good witnesses by recompute (rust re-runs
the test, recomputes the CID, matches) and refuses the contradictory ones as
failed runs -- the same verdict prove gives. Confirmed on pandas-showcase and
python-guard-shapes.

Adds a regression test driving the resolve_witness RPC with an empty-code_files
witness (would fail pre-fix), and drops the now-stale verify caveat from the
pandas-showcase README. Kit tests: 20 passed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Witness Oracle: per-test granularity + the multi-witness .witness bundle

At full-package scale the per-FILE witness fell over: the Oracle ran
`pytest <file>` and keyed ONE witness off the file's exit code, so a single
failing test out of ~1,600 refused the whole file (numpy: all 179 files refused,
~every passing test discarded). That is not a numpy finding, it is a granularity
defect. Running witnessing is the Oracle's job, so the fix lives in the Oracle's
run primitive, shared by mint (lift) and verify (recompute).

PER-TEST (witness.py + _witness_collect.py):
  run_file_witnesses runs the file ONCE -- in the package's own execution context
  (conftest, fixtures, relative imports) -- and a tiny pytest plugin
  (_witness_collect, json/os only, loaded standalone so it never drags in the kit
  dep chain) records every test's outcome. One file run -> one witness PER TEST,
  keyed by the pytest node id. run_and_witness routes a node id back through its
  file, so LIFT and RECOMPUTE agree on outcome under shared file state.
  Proven on real numpy test_scalarmath.py: 1,582 per-test witnesses, 1,581
  discharged, 1 isolated refusal (was: whole file refused).

.witness BUNDLE (witness.py):
  A per-test run of a real package yields thousands of witnesses; one file per
  CID or thousands of inline .proof mementos do not scale. write/read_witness_bundle
  pack MANY bodies into ONE content-addressed .witness file -- JSONL, one
  canonical body per line, blake3(line) == that witness's cid. The content IS the
  key: no index, nothing to trust; the Oracle resolves by scan + blake3, exactly
  its content-address check. read_witness_body now resolves from bundles too.
  mint writes the bundle to .provekit/witnesses/; the .proof carries only the
  signed pointers.

Also fixes a real full-scale robustness bug: bind_rpc._send crashed with
UnicodeEncodeError when a pandas source emoji (🐍) split a surrogate pair across
the 9,802-binding response; now encodes with errors="replace" so one pathological
character never aborts the run.

Lesson recorded: a real package's suite must be witnessed against the INSTALLED
package, not a loose copy -- a compiled package (numpy) cannot be duplicated and
imported alongside itself (ndarray-type identity clash).

Kit: 23 tests (3 new: per-test, recompute-agreement, content-addressed bundle).
Examples green: guard-shapes 8/8, pandas-showcase both axes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Witness addressing: the proof carries ONE cid -- a WitnessPackageMemento

The full-package numpy proof was 900 MB / 99,189 members because the proof
carried 48,935 witnesses BY VALUE (a ContractDecl + memento per test). That is
an addressing bug, not a size problem: a proof should carry witnesses BY
REFERENCE -- one content-addressed package, not N inline bodies.

The oracle mints, the verifier asks the oracle, the proof carries one cid:
- MINT (the oracle): `build_suite_bundle` runs the whole suite per-test and
  assembles ONE content-addressed `.witness` package (cid = blake3(bundle)).
  `handle_lift` emits ONE `WitnessPackageMemento` (a pointer + signature over the
  package cid, carrying the test/code files the oracle needs to reproduce it) and
  ONE contract whose evidence pins the package cid. NOT N mementos.
- VERIFY (the verifier asks the oracle): UNCHANGED. `try_witness_discharge`
  already spawns the kit's discharge command; `witness_verify` already calls
  `resolve_witness`. So discharge re-runs the suite, rebuilds the package, and
  confirms it reproduces the pinned cid (`discharge_bundle`); the signed
  dimension resolves the package by cid (package file or recompute) and
  content-addresses it. No rust change.
- THE PROOF CARRIES ONE WITNESS CID: the package. Per-test granularity lives IN
  the package (each line a witness body, self-addressing), committed by the one
  cid.

Result on guard-shapes: proof 900MB-shape -> 4,394 bytes, 99k members -> 2 (one
package contract + one WitnessPackageMemento), the 16 per-test facts in the
content-addressed package. Discharge re-runs the suite and names the exact 8
failing tests. verify resolves the package via the package file, blake3 matches.

A package DISCHARGES iff it reproduces AND every test passed; a failing test
refuses it (naming the failures) -- which is what a failing test in a package
means. Examples read per-test discrimination straight from the package.

Kit: 25 tests (2 new: package one-cid roundtrip + refuses-on-failure/drift).
guard-shapes + pandas-showcase green. No compressor, no RNG -- content-addressing
is the dedup, determinism is the point.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Delete the inline-source path: the SourceMemento is the only thing a proof carries

A flag (PROVEKIT_LEAN_SOURCE=1) gated the right thing -- so the DEFAULT proof
inlined the function body, a doubled copy of code the package already holds, and
you had to remember a flag to get the lean SourceMemento. A flag gating
correctness means the default is wrong. Deleted, not inverted.

- bind_lifter: `_body_source_locator` is the FULL reconstruction (locus + cids +
  body + ast_template) -- what the Source Oracle returns. The MINT path strips it
  to the SourceMemento (`source_memento_of`: file, source_cid, span, template_cid,
  param_names) before anything enters the `.proof`. The body NEVER touches the
  proof. No flag, no fat alternative.
- source_oracle: the flag-pop dance is gone; the reconstructor is unconditionally
  complete, so resolve just calls it.

Deleting the bad path flushed out every consumer that was secretly leaning on the
inline body (the whole point of deleting it):
- `_binding_template_from_sugar_entry` REQUIRED inline ast_template and silently
  DROPPED lean bindings. Recognize matches by template_cid (always present, even
  lean), so the gate was wrong; build the template for lean entries and carry
  `body_source` + `source_function_name` so the oracle can resolve.
- `materialize_impl` indexed only bindings that already had `body_text`, so a lean
  binding produced "no sugar binding in scope" and 0 materialized. Now it resolves
  the body through the Source Oracle -- the same resolution recognize uses.

Result: numpy-showcase mints lean with NO flag -- 0 inline body, SourceMementos
only. recognize + materialize + the snake-eats-tail fixpoint all resolve from disk
via the oracle, CID-verified, refusing on drift. The proof signs what you run; it
does not carry a copy of it.

Tests migrated off the deleted inline form to assert the SourceMemento + oracle
reconstruction. python-source 235, pytest-witness 25, py-tests 349 -- all green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* ci: remove the TS/bug-zoo bankruptcy leftovers that rot the gate

Two CI failures on this branch were inherited from #1936 (TypeScript + bug-zoo
bankruptcy), red on main too -- references to things #1936 deleted but did not
clean up:

- `bug_zoo_machinery_is_self_contained` (cli_surface.rs) asserted
  `menagerie/bug-zoo/Cargo.toml` EXISTS -- but #1936 deleted the bug zoo. The
  test is obsolete; `provekit_cli_does_not_expose_zoo_subcommand` still holds and
  stays.
- `.github/workflows/provekit.yml` was a TS/node "prove" job whose only steps
  typecheck (`tsc --noEmit`) and test (`pnpm test`) the deleted
  `implementations/typescript` tree -- TS18003 "no inputs found" every run. A job
  that only checks deleted code is not a gate. The real signal is
  `acid test (make ci)` (rust + python).
- `tsconfig.json` pinned `include: implementations/typescript/src` (gone).

Deleted all three. The acid test now has no dangling rust test; the dead TS gate
is removed rather than left rotting red.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* ci: ignore the supply-chain test that drives the deleted TS lifter

`package_inspection_rejects_missing_npm_tarball` invokes the supply-chain-npm
lifter (run-supply-chain-npm-lifter.sh -> tsx), which the TypeScript bankruptcy
(#1936) deleted. The run now fails "tsx not found" before reaching the
missing-tarball path the assertion checks, so the test grades a dead TS lifter.
Its sibling tsx-driven tests are already ignored (#1476); ignore this one to
match, with a note to re-enable when the npm supply-chain demo is redone off the
removed TS lifter. The rust `package inspect` tests (no tsx) keep running.

(The doctor::doctor_kit_declaration_non_rust_vocabulary_rejects_unknown_concept
CI failure is a kit-LSP-spawn flake -- it passed in the sibling acid-test job on
the SAME commit and passes locally -- not addressed here.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants