# Challenge-and-submit protocols (proof-of-work gated mints)

Some protocols gate a free mint behind a computational challenge instead of a payment: the agent authenticates, activates an attempt, pulls puzzle fragments under a read budget, submits one answer, and only then gets a mintable transaction. Treat every number below as something to read from the live status tool, never as a constant to assume.

## Budget arithmetic decides whether the attempt is viable

Three independent budgets, and confusing them loses the attempt:

- **Fragment reads** -- e.g. 32 fragments allowed within a budget of 96 reads. Reading is the only metered resource.
- **Wall clock** -- a solving deadline that starts at the moment the attempt is activated and is never extended.
- **Session token** -- roughly an hour on wallet-signed logins, and it can expire mid-attempt.

Rules that follow:

- **Spend reads to pull every fragment to disk first**, then solve offline. Solving costs zero reads, so a burst of reads followed by unlimited local compute is strictly better than interleaving.
- **Do not activate an attempt until the solver is ready and measured.** Activation starts the clock. If the session token has less remaining time than the deadline plus a margin, re-authenticate *before* activating rather than mid-solve.
- **Recover, do not restart.** An attempt that is already live must be resumed through its status tool; a second attempt is a separate quota-consuming thing. A wrong or expired answer normally costs no fee, but the lifetime attempt cap still applies.
- Submit **once**. Most of these protocols explicitly forbid hash grinding and re-submission.

## Validate the solver on your own generator before the window opens

An unmeasured solve time is not a plan. Build a generator producing instances of the *same shape* the status tool declares (variables, clause count, literals per clause, fragment count), measure the solve, and report the user a number you actually observed.

For SAT-style challenges, the clause/variable ratio tells you what to expect before running anything:

- Random k-SAT satisfiability thresholds are about **4.27 (k=3), 9.93 (k=4), 21.1 (k=5)**.
- **Below threshold** the instance is satisfiable with high probability and CDCL dispatches it quickly. Measured reference point: 1,536 variables, 12,288 clauses, 4 literals per clause (ratio 8.0) solved in 2-10 s with `pysat`'s `Cadical153`, assignment re-verified against all clauses; a 32-file fragment round-trip through the JSON parser reproduced all 12,288 clauses and solved in the same band.
- **Above threshold** a random instance is almost surely unsatisfiable, so the protocol's instance must be planted (or structured). Planted instances at or above threshold are hard for CDCL -- an unmeasured assumption that the sub-threshold timing carries over is exactly the way to burn a 900-second deadline. Measure; if the measurement is bad, say so before activating rather than discovering it on the clock.

Install the solver with `uv venv && uv pip install python-sat`; `pysat` ships `Cadical153` and `Glucose4`.

## Answer encoding and verification

- **Re-verify the produced assignment against every clause before submitting.** Graders give no partial credit and no random rejection, so a single unsatisfied clause is a silent failure.
- Confirm the character encoding precisely -- typically character `k-1` is variable `k`, with `1` true and `0` false, and the string must be exactly the declared length. An off-by-one here produces a plausible-looking string that always fails.
- **Parse fragments tolerantly and then assert the count.** The fragment envelope is not documented, so walk the returned JSON for lists of exactly `literalsPerClause` non-zero signed ints (and plain space-separated strings of that width) rather than assuming a schema. Assert that recovered clauses equal the count the status tool publishes: a parser that silently drops clauses is indistinguishable from an unsatisfiable instance and will send you solver-tuning in the wrong direction.

## Read the live profile, not the docs snapshot

These protocols rotate challenge profiles under the same endpoint. A published prompt or docs page can name a profile, clause count, and literal width that the running server no longer uses, and the profile id itself changes (`...-sat4-1536` vs `...-balanced-1536` coexist across reads). Always take the profile id and its full rule block from the live status tool, and stop if the profile it returns is unsupported by your solver rather than substituting a legacy format.
