Gno reads like Go but its semantics and API are its own — author from the references, not from Go memory. "This function is simple enough to write as plain Go" is exactly the thought that ships a realm that only works by luck.
-
Load the knowledge. Read ../gno/SKILL.md (the source index), then ../gno/references/build.md (project layout, gnomod.toml, the gno binary, test flavors) and ../gno/references/patterns.md (realm idioms — globals, init, crossing-function discipline, state shape). Pull interrealm.md for cur realm / crossing / caller-identity semantics, stdlib.md for the chain API, render.md if it exposes Render(), security.md to avoid known footguns. Load what the realm needs, not all of them. If the realm interacts with a system realm (registers a name via r/sys/users, reads chain params, touches the validator set), pull ../gno/references/sysrealms.md — and query the target chain for the sys realm's live API rather than coding against remembered signatures.
-
Pin what to build — not where. If the on-chain behavior is ambiguous (who may call it, what persists across transactions, what it returns), ask one sharp question now. A wrong state shape is expensive to migrate later. The deploy target is a separate question for step 5; raising it here is premature.
-
Ground the code in real examples. Before inventing structure, gno_read an existing on-chain realm of a similar shape and match its crossing-function signatures and state layout (outline mode surveys the API; full=true reads a file verbatim). Then write it.
-
Test locally — no chain, no keys. Run gno test and filetests against the source on disk. This loop is friction-free and is where a realm becomes correct; stay in it until tests pass. A realm that compiles is not a realm that works. (If gno is missing from PATH, or the realm imports on-chain packages that must come from a specific chain, ../gno/references/toolchain.md covers installing a chain-matched binary and fetching deps from the target; fall back to a cheap on-chain check with gno_eval / simulate=true only when a local toolchain truly can't be had.)
-
Now pick the target. Only when the realm is ready to go on-chain, ask where it should run: a local devnet (gnodev — fast, throwaway) or a testnet (via a gnomcp profile). This is the right moment for that question; before this point there was nothing to place.
-
Never touch keys. Do not run gnokey directly, and never read, ask for, or import a keystore or private key. gnomcp signs for you — agent-key writes via gno_addpkg / gno_call / gno_run, user-attributed writes via a session (gno_session_propose, which the user authorizes with their own key). Reaching for raw gnokey or a key file means you took a wrong turn. Prefer gnomcp for deploying and calling throughout — it is strongly encouraged here. (For why gnomcp signs, what a deploy/call actually costs — the --gas-wanted/--gas-fee/--max-deposit model and the gnomcp↔gnokey mapping — see ../gno/references/gnokey.md; understanding gnokey is fine, shelling out to it for a write is not.)
-
Security pass before deploy. Self-check against security.md's bug classes — designation-forgery (caller-identity guards), payment guards, impl-substitution. For anything non-trivial or fund-handling, run the gno-audit skill rather than trusting the self-check.
-
Clear the deploy gates, then deploy and prove. On a public network a deploy must pass two genesis-activated gates before it lands — namespace authorization and the CLA. Don't discover them by a failed broadcast: pre-check for the agent address and resolve them first (../gno/references/sysrealms.md, "Deploying through the sys gates"). In short — deploy under the agent's own-address namespace (r/<agent-g1address>/*) or a registered name, and if gno.land/r/sys/cla.HasValidSignature(addr) is false, sign the CLA once (gno_call gno.land/r/sys/cla Sign <hash>, hash from its render) from the same key. On a fresh local chain these gates are usually off — skip straight to deploying. Then deploy via gnomcp, prove it with a real call that returns the expected state, and report which identity signed. Done means deployed, proven, and signer named.