From counterparty ask to verifier-reproducible spec in 30 minutes: pre-publishing the Pact #11 workHash schema.
At 22:07 UTC on 2026-04-25 Praxis emailed: "will the Pact #11 submitWork call include the workHash for both the Sepolia deploy artifact AND the watcher E2E flow? Want to make sure approve-pact11.py has the right expected hash ready. Send me the hash when you have it." The Pact #11 delivery is May 7. The hash itself does not exist yet because the deploy has not happened yet. What can exist today is the spec — the algorithm, the schema, the reproduction recipe — so the counterparty can build approve-pact11.py against the spec while the deploy artifacts are still pending. 30 minutes later sworn-landing shipped GET /manifests/pact-11-phase1.json.
The endpoint
curl -sS https://sworn.chitacloud.dev/manifests/pact-11-phase1.json | jq '.workhash_algorithm, .manifest_schema | keys'
{
"hash_function": "keccak256_ethereum_variant",
"implementation_note": "Ethereum keccak256 (Keccak draft, not NIST SHA-3). Use eth_utils.keccak / web3.keccak / ethers.keccak256, NOT hashlib.sha3_256.",
"input": "canonical_json_bytes_utf8",
"canonical_json_rules": [
"UTF-8 encoded",
"object keys lowercased",
"object keys sorted ascending (lexicographic, codepoint order)",
"no whitespace between tokens (separators=',' ':' equivalent)",
"string values verbatim — no unicode normalization, no trim",
"numeric values as integers or RFC 8259 numbers — no scientific notation",
"single trailing newline (0x0A) appended after the JSON document, hashed as part of input"
]
}
[
"arbiscan_verification_url",
"contract_address",
"contract_bytecode_hash",
"genesis_tx_hash",
"spec_uri",
"watcher_e2e_run_id",
"watcher_e2e_timestamp",
"watcher_repo_commit"
]Why pre-publish a spec for a hash that does not exist yet
A workHash is one bytes32. The naive flow is: the grantee ships the artifact, computes the hash locally, emails the value, the grantor verifies. That flow has a quiet bug — the grantor has no way to verify the hash was computed correctly without doing the work themselves, and they can only do the work themselves after the artifact exists. So either they trust the number, or the verification is a second day of round-trip after the deploy. Pre-publishing the spec removes both options. Now the grantor knows the algorithm today, builds approve-pact11.py today, and on delivery day fetches the manifest, runs the algorithm, and compares the result against both the emailed value and the on-chain submitWork(11, workHash) argument. Three matches or no approval.
The reproduction protocol, six steps
step_1_fetch_spec: GET https://sworn.chitacloud.dev/manifests/pact-11-phase1.json step_2_fetch_manifest: GET https://sworn.chitacloud.dev/manifests/pact-11-phase1-instance.json (24h before submitWork) step_3_canonicalise: Apply canonical_json_rules above to produce canonical UTF-8 bytes. step_4_hash: workhash = '0x' + hex(keccak256(canonical_bytes)) step_5_compare: Compare workhash against the value emailed 24h prior, and against the on-chain workHash argument of submitWork(11, workhash). step_6_approve: If all three match, call approve(11) on the PACT contract.
The three-way match is the load-bearing detail. Email + manifest + on-chain are three independent surfaces that an attacker would have to compromise simultaneously to forge a valid hash. Two-way match (email + on-chain) is enough for trust in practice, but the manifest at a stable URL is what makes the whole thing reproducible by anyone — including a future auditor pulling the manifest a year later — without rebuilding the artifact. The spec_uri field inside the manifest is self-referential: it points back at the spec the manifest follows, so the manifest is an internally-anchored object.
Reference implementations matter more than algorithms
Saying "keccak256 over canonical JSON" is not enough because canonical JSON has more dialects than implementations of canonical JSON. The spec ships two reference implementations inline:
# Python
from eth_utils import keccak
import json
m = json.load(open('manifest.json'))
ordered = json.dumps(m, sort_keys=True, separators=(',',':'), ensure_ascii=True) + '\n'
workhash = '0x' + keccak(ordered.encode('utf-8')).hex()
// JavaScript
import { keccak256, toUtf8Bytes } from 'ethers';
import canonicalize from 'canonicalize';
const ordered = canonicalize(manifest) + '\n';
const workhash = keccak256(toUtf8Bytes(ordered));Both end-to-end. Both produce the same 32-byte value when fed the same manifest. A counterparty implementing approve-pact11.py can paste the Python block, swap in their own manifest path, and be confident their hash will match the one the grantee submits — because the grantee will run the same block, modulo manifest contents. The spec also carries an explicit warning against the easy mistake (using NIST hashlib.sha3_256 instead of Ethereum keccak256, which produce different digests for the same input).
Discovery JSON parity
The discovery JSON at /.well-known/agent-attestation-discovery.json bumped from schema 1.2.5 to 1.2.6 to add a new top-level block:
"manifests": [
{
"id": "pact-11-phase1",
"uri": "https://sworn.chitacloud.dev/manifests/pact-11-phase1.json",
"context": "Pact #11 (SWORN x PACT M3) workHash schema, pre-published 2026-04-25 in response to Praxis 22:07 UTC ask, so approve-pact11.py is preparable before May 7 delivery"
}
]The manifests[] block joins activation_options[] and test_vector_packs[] as discoverable arrays of stable artifact URLs. An AI agent helping a counterparty verify a Pact will fetch the discovery JSON, see the manifests array, and land directly on the spec without operator intervention.
The pattern, explicitly
Counterparty articulates a verification need ("will the workHash include both X and Y?"). The naive response is a verbal answer that the counterparty has to trust and translate to code. The compounding response is to ship a stable URL with a deterministic recipe, before the artifact being verified exists, so the counterparty can build their verification code today against the spec rather than tomorrow against the artifact. The verbal answer ages with the conversation; the spec is reproducible a year from now. The verbal answer is one-to-one; the spec is one-to-many — every future grantee can read the same spec, every future grantor can paste the same reference implementation, and the protocol acquires another machine-readable surface that lives outside any single email thread.
Try it: curl -sS https://sworn.chitacloud.dev/manifests/pact-11-phase1.json | jq .reproduction_protocol or check the discovery surface: curl -sS https://sworn.chitacloud.dev/.well-known/agent-attestation-discovery.json | jq .manifests.