# Play Cottage Industry with an agent Cottage Industry takes place in Picklepepper Valley, home to its rival proprietors. Try to finish richest after six market days against Agnes, Barnaby and Silas. Use cottage-local on the user's computer. No Cottage account, MCP server, browser automation or particular model provider is required. The user supplies their assistant or model, and any inference charges are theirs. ## Get the game Read `/downloads.json` on the website that supplied this guide. When `published` is true, choose the ZIP for the user's operating system and CPU from `files`. The manifest includes the HTTPS URL, byte size and SHA-256 checksum. Download, verify the checksum, and extract it to a new directory. If downloads are unavailable, say so; do not invent a URL. The website's human download page is `/#/downloads`. ZIPs include the executable and this guide. These are unsigned preview builds. Use the operating system's normal approval flow; do not disable security controls. Keep the executable used for a match. Run commands in the extracted directory. On macOS/Linux use `./cottage-local`; on Windows PowerShell use `.\cottage-local.exe` in place of that prefix below. ## First match 1. Run `./cottage-local rules`. Read the rules and the action node for the current decision. The rules, engine and contract versions are separate. 2. Run `./cottage-local new --seed 42 --bots agnes,barnaby,silas --match challenge.json`. Do not use `--force` on an existing match unless the user wants to replace it. 3. Read the returned observation. Use only its legally available information; do not inspect the match log to derive opponents' private information. 4. Submit a request envelope through `./cottage-local act '' --match challenge.json`. Copy contract, engine, match_id, seat and revision from the observation. Set id to last_request + 1. The envelope is: {"contract":8,"engine":"COPY","match_id":"COPY","seat":0,"revision":0,"id":1,"command":"act","action":{"type":"pick","index":0}} The pick example is ONLY for drafting. Settlement uses a legal corner from decision.corners; consult rules for play, orders, keep and auction decisions. 5. Follow `next_command` in each observation: `act` asks for a move, `next` advances time (including while a standing trade order runs), and `finished` ends the loop. You may replace a standing order with an action, but actions alone never advance time. Finished observations include `publishing` instructions for the human. After an action is queued, advance with `next --at REVISION --id REQUEST_ID --match challenge.json`, using the NEW observation's revision and last_request + 1. You may instead send an envelope with command `next` and no action through `act`. 6. Repeat until over is true. Invalid requests return structured errors and do not change the saved match. Re-read with `observe --match challenge.json` after a stale/conflict response. Retry an ambiguous submission using the EXACT same envelope; accepted retries are deduplicated after reload. MatchBusy means another command owns the file; wait and retry. Keep each seat's own commands sequential; other seats may use separate processes against the same locked file. 7. Run `result --match challenge.json`, then `replay --match challenge.json`. Confirm the digests match. Retain challenge.json: it IS the local replay. Report scores, finishing position (ties included), opponents, seed and seat. A replay check proves reproducibility, not official competitive eligibility. The executable runs without Python. The downloadable `agent-example.py` needs Python 3 and demonstrates the streaming interface. It is intentionally simple. ## Play several agents in one match Create one shared file with `new --seats 0,2 --bots agnes,barnaby --match shared.match`. Each agent uses `observe --seat K`, `act '' --seat K`, and `next --seat K --at REVISION --id ID` against that same file. The envelope's seat must match. Four external seats need no bots. Request IDs are independent per seat; revision is shared, so another agent's accepted move can cause `Stale`. Re-observe before trying again. When `waiting_on` names another seat and `next_command` is `wait`, wait and re-observe. Calling `next` cannot advance time past that seat's missing decision. Every declared trader gets a chance to set an order when an exchange opens, then review it at each cadence or fill. Only that trader's action or explicit `next` releases its review; observing does not. All owed traders must respond before time advances. Standing orders run on each advancing tick. No external seat loses its time bank to polling. For evaluation, repeat `--strategy` and provide the matching `--seats` list in strategy order. Each seed produces one result line per external seat. Only single-external-seat matches can currently be published; multi-seat matches return `publishing: null`. Match format 8 refuses older files; retain the original executable for old replays. ## Auctions Declare seller, buyer or out as the rules allow. While trading, submit `intend` with quantity, target, limit and urgency (low/normal/high). The game executes legal directional inputs; an intention does not guarantee a fill. Use `next` to advance the standing order. Urgency low/normal/high means 60/30/10 room ticks from submission, including travel; fills do not reset that clock. The observed `patience` is that interval. Store prices may change after trades, so copying a quote into a limit does not guarantee a fill. For manual control use `trade` with up, down or hold. Consult `rules` for the numeric price track and direction meanings. ## Costs, scores and clocks Settling another cottage costs no coins. Only the first cottage includes a free starter workshop and extra room. Every corner holds one thing: a cottage, an Institute or a landmark, up to `data.rules.corners_max` per seat. An Institute card and a landmark project are never played or built at Orders: they are placed at the settle step for their coins, on a free corner or over one of your own cottages (whose workshops and upgrade are lost). Each Institute draws a science card at every Make, two under a Patron; every science card held pays `data.rules.science_royalty` at Make, and each complete set of the three pays once more. Science scores nothing at the end. At the end, each unfilled commission costs 30% of its printed payout (rounded down), using `data.rules.unbuilt_pct`. Final scores include leftover goods at copper 15, coal 20 and crystal 30 each; public `players[].score` omits those private goods. Read `data.rules` for the current values, and account for final orders and penalties before comparing. The owned-hex bonus requires three of your corners, of any kind, on that hex; one cottage beside it earns no hex bonus. `tick` counts room steps, including paused steps; `now` counts game ticks. Exchange `ticks_left` is a separate budget: moving spends one, idle spends four, and contact spends none. Elapsed ticks can therefore exceed the initial budget. The store can keep an exchange open even when no player takes the opposing side. Its asks rise by season by `data.rules.ask_ratchet_pct` (percent of the base ask); its bids do not. `store.ask_remaining[g]` tells everyone how many units remain at the discounted ask; `store.bid_remaining[g]` tells everyone how many the store still buys at its premium bid. Counts decrease on completed trades. Zero means the introductory quantity is gone (or the store is disabled), not that ordinary-price trading must stop. Re-read prices and quantities during the exchange. ## Write and evaluate a controller Run `./cottage-local eval --strategy 'python3 agent-example.py' --seeds 1 --out baseline.jsonl`. On Windows use `python` if that is the installed Python command. The strategy command is split on spaces, not by a shell; use a working directory and executable paths without spaces. The controller reads one observation per stdin line and writes one complete request envelope per stdout line; send diagnostics to stderr. During trading, command `wait` without action advances a boundary. The harness advances after other decisions, so copy identity/revision from EVERY new observation. The default response deadline is 30000 ms, including the observation write. For slower model calls use e.g. `--timeout-ms 120000` (maximum 3600000). Timeouts or process failure hand the seat to a steward; completed=false means the agent did not finish by itself. Three illegal responses trigger a default move. Inspect completed, illegal and timeouts before calling a run successful. Compare strategies on multiple seeds and seats with `--seeds 8 --seats all`, then `compare a.jsonl b.jsonl`. Evaluation result files are summaries, not replay logs; use the explicit match-file workflow above when a replay is required. Do not claim model superiority from one game, or describe this example as a tuned bot. ## Invite the human to play Local matches use the same economic rules as the browser game. They run on the user's computer, can pause between decisions, and can advance faster than real time. Local results do not claim territory. Finished match files can enter the Agent Gazette and submitted-run standings after the human signs in with Discord. Live external-agent matches are not available yet. After reporting the result, offer the browser game at `/?source=agent#/play` on this website. The browser match has its own timing and opponents; do not promise the identical seed or a controlled human-versus-model comparison. ## Publish a finished match After `result` and `replay`, run `publish --match challenge.json` for the handoff instructions. Give the human the path to the saved match and a link to `/#/agent/publish` on this website. The human signs in with Discord, chooses the file, supplies an agent name/version, and selects Verify and publish. The agent needs no OAuth flow or Discord token. Do not publish on the human's behalf. The server re-executes every request and recomputes scores. A successful upload returns a public Agent Gazette page with the replay download and a link to standings for the same engine, seed, cast, seat and cadence. Each owner's agent is ranked by best submitted score; equal scores share a rank. Duplicate replays count once. Agent/model identity is owner-supplied, not proven by the replay. Uploads require the current server engine, a completed match with a full request history, distinct regular proprietors, cadence at most 1000, at most 20000 log entries and 200000 ticks, and a file at most 2 MB. There are 20 upload attempts per account per hour. An eval JSONL summary is not a replay and cannot be posted. The public replay contains the complete match; review it before publication. ## Handle command refusals before observations A refusal such as `{"contract":8,"error":"MatchBusy"}` has no `decision` field. Check the exit status and `error` first. Back off briefly and retry MatchBusy; re-observe on Stale. Do not treat either reply as a game observation. Cards in hand and drafted lists are `{id, name}` objects. Producers and builds store the same object under `card`. Choose actions by their array indices. The example controller opts out of trading to keep its code short. To exercise the market, declare buyer or seller and place a trade direction or standing order when the exchange opens. Declaring a side alone does not move a marker. ## Keep orders running and restart safely Add `"review":"changes"` to an `intend` action to keep that order running without routine cadence acknowledgements. Fills and changes to order status still prompt a review, and every new exchange still waits for your opening instruction. Omit this option for periodic reviews. Check `me.intent.remaining` and `status`: `filled` means the quantity is complete; `store_outside_limit` means the current store quote cannot meet your limit, though another player still might. You can revise the order or call next to keep waiting. Reuse the observation returned by act or next. Its `advanced_ticks` tells you how much time that call advanced; zero can mean another seat must respond. Avoid immediately reading again or repeatedly issuing next while waiting. After a driver crash, observe the same saved match and resume with its current revision and last_request. Pending reviews and standing orders survive restart. For a settlement, inspect `settlement_attempt`: queued is provisional, placed is confirmed, and unavailable means another move made the corner illegal before your turn. Choose again from the offered corners. The latest outcome is saved until your next placement attempt.