Co-builder rules. Read alongside the build document.
docs/RULES.md maps every rule from the rejection-notice compilation to what
this project does about it, and names the four that found something broken here.
Read it before changing a contract. The four worth holding in your head:
A refusal must leave the refused party somewhere to go, and the way out
must not be asking the same question until the answer suits.
Ask the same question in both presentation orders, inside one block, and
resolve the disagreement in the VALUE, never in the comparison.
A nondet block returns a flat dict of strings. A bool fails in the calldata
encoder outside the contract, with no traceback to read.
A mutation runner that scores a kill from an exit code alone will report a
perfect run while testing nothing. It happened here. Every kill names the
test that produced it, and the table is not written if anything escapes.
Two contracts. The escrow is deterministic and was finished before the
dispute contract existed.
Nothing is read from the live internet at judgment time, by either contract.
Three frozen strings from the parties: promise, request, response. The
fourth block in the prompt is timing, written by the chain, and no party
can set it.
Three verdicts only. Only a clear verdict moves funds.
The validator compares the verdict field and never the reason string.
The happy path adds zero latency and runs no judgment.
The evaluation set was committed one commit before the judgment contract,
and cases-v2.json was committed alone before the runner could read it.
git log proves the commit order, which is not the same as proving when
a file was written. Say the provable thing, and name the gap.
In: escrow, dispute, one seller endpoint, one buyer agent, one feed,
the committed evaluation cases, the judgeability gate.
Out: batching, seller bonds scaled to volume, x402r integration, dynamic
pricing, marketplace integrations, multi endpoint, reputation index.
Nothing in the Out list is missing. It belongs under a heading called Later, in the README, and nowhere else.
Never guess a GenLayer API. Read the pinned SDK on disk. The Depends header
resolves through py-genlayer/<hash>/runner.json to a std lib hash, and two
std libs ship with incompatible APIs, so follow the header rather than
picking a directory.
Never put a model call in escrow.py. A test asserts it.
Never read the live web from a contract.
Never use strict_eq for the judgment call.
Never compare the reason string in the validator.
Never store a raw dict or list. Never use a float. Never leave a storage
field unannotated.
Never default to a verdict when parsing fails. Raise a named error.
Never edit an evaluation case to make a run pass. Narrow the question.
Never hardcode a protocol parameter such as committee size or appeal charge.
Run genvm-lint after every contract edit, with PYTHONIOENCODING=utf-8.
Read the deployed schema before wiring any client.
Distinguish accepted from finalized in the UI. They mean different things.
English. The spaced hyphen is the only connector: no em dash, no en dash, no
separator dot, no ellipsis. python scripts/check.py fails on all nine banned
characters and is wired into python scripts/test.py, because intending to
remember this has never worked.
Model output quoted from chain state is a record, not house copy. Do not normalise its punctuation: that would misrepresent what was actually stored.
Never announce reduced scope in the pitch. Verify every number against a source before publishing it.
If a change does not serve the demo sentence, say so before writing it. If a design choice creates an exploit, name the exploit. One paragraph, with the reason. Do not repeat it after I decide.
The demo sentence:
An agent pays, receives a worthless response, contests it, and has its money back in under a minute, with no human in the loop.
python scripts/test.py everything: style, lint, tests
python scripts/mutate.py --table docs/MUTATIONS.md 32 defences, verified
python scripts/verify.py deployed bytes diffed AND linted
python scripts/evidence.py refusals on chain, hashes recorded
python scripts/deploy.py redeploy and rewire, one command
python scripts/demo.py the whole demo, both paths
python eval/run.py --runs 3 the published accuracy number
python seller/main.py the endpoint, port 4501
python agent/run.py --mode stale the contested path
cd web && npm run dev the feed, port 4500
Studio drops TLS handshakes in bursts. genlayer_py posts once per RPC call
and turns a dropped connection into a hard error, so one drop fails a whole
deploy and it reads as a broken contract. shared/chain.py installs a session
with connection layer retries. Do not remove it: without it a probe failed ten
times running, and with it the same probe went twelve for twelve.
Studio persistence is temporary. Run scripts/deploy.py every morning. It
recreates the whole state in one command, rewrites deployed.json and
web/.env.local, and the feed picks the new addresses up on its next read.