A desk on Robinhood Chain
THE CURB
The position · Mechanism · decision records · §16, G02 · proposed, not decided

Launch checklist

who does what, in what order, with which command; what each tool refuses when a row is not met; what the site shows once it is — a checklist that executes the token record, deciding nothing itself.

Status: a checklist, not a decision. Prepared 13 September 2026. It executes the token record (decided by the product owner, 12 September 2026, Robinhood Chain included). It follows the deployment plan and the operator policy, both still proposed. The treasury creation and signer record are complete. It establishes no approved public CompanySeries deployment, no configured CURB/CreditDesk deployment and no independent review.

Nodes.

Measured 13 September 2026:

Who
  • —The product owner decides the signers, the launch and the pool. Decisions are recorded by name and date; the site never upgrades one itself.
  • —The signers: three people with their own Robinhood Chain wallets. They own the operator's Safe, 2-of-3 as the policy proposes: the treasury every top-up goes to.
  • —The reviewer: a named person, not the record's author. They check the tool's output against the chain and sign with name and date. Without that name the deployment tool refuses.
  • —The operator holds the tools' shell, the deployer key and the Railway app service's variables. The key is ETH-funded on Robinhood Chain for gas, and never in this repository or a chat.

One person may hold several roles; the records say which.

Where it stands

Done: everything under what exists today in the token record, plus the Safe tools and the local-chain rehearsal in CI. Two adversarial site-code reviews closed their confirmed findings (commits 54ab36e, fc3df1d). A deployed desk is verified against contracts/evidence/CreditDesk.build.json, from source commit 06623ed.

Done 13 September 2026: rows 1 and 2 (contracts/evidence/safes/safe.4663.json), owners and threshold read back through the operator's endpoint and the public node. The deployer wallet exists and is funded.

Not done: rows 3 onward. Until then the site shows NOT_CONFIGURED on /services and /api/credits, and not done in the order of work.

The public stage line (13 September 2026). The product owner asked for it the day the treasury went live. The front page's kicker reads Stage · mainnet · Robinhood Chain. The line beneath is derived from the record by lib/launch/status.ts: the Safe's evidence file, the CURB_CREDITS line, the last code and rate reads, credited top-ups. It states what is on chain (the treasury) and what is not (the token, the desk, the position series). It advances as rows are done:

  • —a valid treasury creation record;
  • —a configured desk;
  • —matching code for the configured token and treasury;
  • —a rate read;
  • —a credited top-up tied to the current chain and desk index.

Code and rate reads older than thirty minutes do not advance it: a display rule, not an uptime promise. Missing evidence means unconfirmed, not proof that nothing exists on chain. The footer (BRAND.stage) and the series' stage line say the same. None of it makes the eight refused claims or says anything is sold.

The rows

Order. Before row 3, finish what TOKEN and the mainnet dossier require: the independent-review scope and its actual funding, launchpad/terms/authority review, and the approved proceeds formula. Rows 1–2 are completed treasury actions; do not create another Safe to repeat them. Run rows 3–10 after their decisions, then 12 → 13 → 11 → 14 → 15. A supported, configured, freshly read pool precedes the first public-chain payment test. Unpriced-payment recovery belongs in an isolated rehearsal. Row numbers are stable references, not permission to skip these dependencies.

№WhoWhatNeeds firstCommand or actionRefused whenDone shows as
1Product owner, signersName the Safe's owners (at least three) and threshold (2, per the policy)Three or more people with own Robinhood Chain wallets, none a signer for any issuer or custodian of a series' components; addresses in the operator policy by name and date, published (register A4)A decision, not a commandplan-safe.ts refuses fewer than three owners, an owner listed twice, an address that is not 20 bytes of hex or fails its EIP-55 checksum, a threshold under 2 or over the countPolicy names them; A4 closes. Done 13 September 2026: thecurb 1 0xC33DAfAC1c65a92e956519Aa73E29a0b8c39214B, thecurb 2 0x5657dF9f072C536FcdE00BE2eF86c2240E4f8a53, thecurb 3 0x5cc762A11b05F9cCdaE3839C56F5Beb14C40F15D
2OperatorCreate the SafeRow 1; a funded walletnode scripts/plan-safe.ts <owner> <owner> <owner> --threshold 2 simulates on the node, then prints the to and data of createProxyWithNonce on Safe's factory and the predicted address. Any funded wallet can send them. Or add --send --reviewed, with DEPLOYER_PRIVATE_KEY in the shell, to send, await the receipt, read owners and threshold back, compare them with what was asked and write evidence/safes/safe.4663.json. Check the explorer too (getOwners(), getThreshold())The node does not answer, or answers another chain id; Safe's factory, singleton or fallback handler has no code; a contract already sits at the predicted address (that --nonce was used with these owners — choose another); the simulation disagrees with the prediction; with --send: no --reviewed off the local chain, no key in the shell, a sender with no ETH, an evidence file from an earlier creation, a receipt that did not succeed, a Safe that answers other owners or another threshold than askedDone 13 September 2026: 0x4E69723F9Ba9fA2C9842d77b240b2C0F601ac219, block 61,691,826; three owners, threshold 2, read back as asked. Acts through safe-tx.ts --safe … --to … --data …: owners send approveHash; with --approved-by <owner>,<owner> after two approvals, anyone sends execTransaction (details)
3Product owner, operatorLaunch the token on PONS v2 (decided 17 September 2026)Venue terms pinned: node scripts/pons-preflight.ts --launcher <the sender> writes evidence/pons/preflight.4663.<block>.json (launchFeeWei, config 0, expectedEconomics digest). Pair: native ETH, decided by the product owner 17 September 2026 (config 0; pairToken the zero address; ETH / USD feed quote). The product owner's three open decisions (TOKEN): counsel on Terms of Use §11 and §3; who sends; a venue unaudited by its own statement. Sender funded with fee and gas. Launch record reviewed by a personnode scripts/pons-launch.ts records/pons-launch.4663.json checks the record against the token record: no creator tax, no buyback, the fee recipient the operator's Safe per evidence/safes/safe.4663.json (owners and threshold re-read), a pinned fee, a pinned non-zero digest. It re-reads the venue in one block (launching open for the sender, fee and digest unchanged, hook and PoolManager as recorded, pair approved, sender's balance) and simulates the launch. Nothing is sent or written. --calldata --reviewed journals the intent (the Safe as sender, predicted token and curve) in evidence/pons/launch.4663.json and prints bytes and value for the Safe to send via safe-tx.ts; --receipt <hash> then reads the launch back and completes the evidence. --send --reviewed with DEPLOYER_PRIVATE_KEY in the shell journals the intent (sender, nonce, prediction) before broadcasting, sends exactly the pinned fee, records the hash before awaiting the receipt, and compares the token the factory records field by field with what was askedA record naming the example; an empty name, symbol or description; a fee recipient other than the recorded Safe, or a Safe whose owners or threshold moved; a creator tax, a buyback, a zero digest; a reviewer not named; off the local chain, no --reviewed, or uncommitted launch tooling; a venue term that moved since the preflight (the fee or the digest); canLaunch(sender) false; a sender holding less than the fee; an existing journal — completed, or an intent the chain does or does not yet show (never a second send over it); a receipt without exactly one TokenLaunched from the factory; a token the factory records differently from what was asked (never reported "as asked" unless every field is)evidence/pons/launch.4663.json: plan, transaction, token as the factory recorded it; then row 4
4OperatorRead the token into the recordRows 2–3. During the curve, node scripts/pons-status.ts <token> gives phase and quote collected against the graduation threshold; the desk reads no curve and quotes nothing until the pool existsnode scripts/record-token.ts <token> --treasury <safe> reads name, symbol, decimals, supply, code hash, the EIP-1967 proxy slots, owner() and paused() at a stated block. It writes them (tokenAsRead) into records/credit-desk.4663.json, with rpcUrl the public node, priceSource null, reviewedBy empty. Without --treasury the treasury is the zero address, which the deployment tool refusesThe node does not answer, or answers another chain id; the token has no code; it does not answer symbol(), decimals() and totalSupply(); a function the node would not answer is a refusal, never a fact written downRecord file; a note if the token is behind a proxy (its admin can change its code; the desk does not control it)
5ReviewerReview the actual recordRow 4; the independently reviewed contract/buildCheck tokenAsRead and source/authority facts against the chain. Verify the current treasury Safe identity, owners, quorum and controls against row 2's record. Attach review evidence; only then fill reviewedBy and reviewedAtBlank review fields are refused. A typed name alone cannot prove review; nonempty treasury code alone cannot prove a SafeReviewed record linked to source/build and report; alone, it does not close A2's independent contract review
6OperatorDry runRow 5node scripts/deploy-credit-desk.ts records/credit-desk.4663.json --dry-run --reviewed (off a local chain, --reviewed is needed even for the dry run, on top of the record's review)In the tool's order: a misspelt flag (--dryrun), a --flag=value, an unknown flag — refused before the record is opened, never a real deployment; a record that cannot be read as JSON; no reviewer (row 5); a network that is not a profile the site knows, or a chain id that is not that profile's; a token or treasury address missing or zero, or the two the same; decimals outside 0–36; a malformed priceSource; on any chain but 31337, no --reviewed; then the node: it does not answer, or answers another chain id; the token has no code, does not answer symbol(), decimals() and totalSupply(), or answers other decimals than the record; the treasury has no code (the treasury is the multisig, not a key); the pool, if recorded, fails row 12's checks; the build record is missing, or the compiled contract is not the committed build record (rebuild with npm run build; npm run check:build says whether the record is this source's)dry run: every check passed; nothing was sent, with the constructor arguments (the token, the treasury) printed
7OperatorDeployRow 6; DEPLOYER_PRIVATE_KEY in the shell: ETH-funded, operator-held, never in this repository or a chatnode scripts/deploy-credit-desk.ts records/credit-desk.4663.json --reviewed writes evidence/deployments/credit-desk.4663.json with the transaction hash and a pending note before awaiting the receipt (a cut-off leaves a note, not a second deployment). It then rewrites the file with address, block, transaction and constructor arguments, and prints the CURB_CREDITS line on stdoutEverything row 6 refuses; evidence/deployments/credit-desk.4663.json already exists (a desk was deployed from this record before — read it first); DEPLOYER_PRIVATE_KEY unset or not 0x plus 64 hex digits, or holding no ETH on the chain (checked last, after every read, before anything is sent); the transaction did not succeed — the file then still holds the pending note with the transaction hash, and the next run is refused until a person has read it and moved it asideEvidence file, committed; CURB_CREDITS line for row 8
8OperatorConfigure the siteRow 7; store schema current (npm run db:migrate with CURB_POSTGRES_URL, idempotent)Set CURB_CREDITS on the Railway app service to the printed line (a bare JSON object; priceSource null until row 13). A Railway variable change redeploys the service and is live when that deployment is. Set CURB_TICK_SECRET on the deployment (without it production /api/tick refuses requests with 503; local development may omit it), and the same value, with CURB_TICK_URL, in the repository's secrets for the scheduler. CURB_RPC_URL on the deployment is the keyed dRPC endpoint (lb.drpc.live, confirmed on Railway 21 September 2026) and CURB_RPC_ROUTE=all: every read goes to it first and falls back to the public node (see the header)/api/credits answers CONFIG_INVALID with the reason: not JSON, not an object, an unknown network, a malformed token, desk or treasury address, the token and the desk sharing an address, the treasury equal to the token or the desk, a fromBlock that is not a non-negative integer, a priceSource that is not an object or null. A second desk in the same line (a redeployment) restarts the index from its own fromBlock; the earlier desk's credits stand under its address/api/credits: CONFIGURED, with desk, token, treasury; /services: configured; code not yet verified until the next tick
9The tickVerify the codeRow 8; the scheduler (GitHub Actions, every five minutes, CURB_TICK_SECRET)Nothing to do: the next tick compares the desk's code outside the two immutable slots with contracts/evidence/CreditDesk.build.json, and the slots with the record's token and treasuryA MISMATCH is a DARK condition on /api/state and, one tick later, on the webhook (a tick delivers alerts before it runs the desk); a MISMATCH, a code read the node did not answer (UNREAD) or a missing build record (NO_BUILD) all hold the index — nothing is indexed or credited from the desk — and /services composes no top-up bytes and says do not send a top-up on the strength of this pageMATCHES the build at commit 06623ed…; the immutables hold the token and the treasury above on /services; step 3 of the order of work done
10OperatorVerify the source on the explorerRow 7Verify contracts/src/CreditDesk.sol at commit 06623ed (the build record's sourceCommit; unchanged since) on https://robinhoodchain.blockscout.com: solc 0.8.30, EVM version prague, viaIR, optimizer on with 200 runs, bytecodeHash: none (the settings in contracts/hardhat.config.ts), and the printed constructor arguments. A third place to look, for a reader who trusts neither this site nor the repository—Verified source at the desk's address
11OperatorFirst small payment and service proofRows 9, 10, 12, 13; current code/config/rate readiness; separate transaction authorization and test budgetCreate a desk key and keep it private. Pay from an operator-owned funded wallet other than the treasury. On /services, review the exact amount, quote age/buffer limits, approval and top-up bytes. After the authorized transaction, reconcile receipt, treasury balance delta, index confirmations and credited dollars. Complete a paid HTTP call and a successful webhook with that keyDeltaWrong, transfer/allowance failures, absent or stale readiness hold the experiment. A missing index row is investigated, not treated as a zero balance. One transfer proves its observed path/amount, not every future token behaviourTransaction and confirmed credited row on the current chain/desk; HTTP/debit/webhook evidence. Any UNKNOWN settlement recorded, reconciled without blind retry
12Product owner, operatorThe poolA pool the reader knows on Robinhood Chain: constant-product pair (uniswap-v2-pair: getReserves(), Sync); concentrated-liquidity pool (uniswap-v3-pool: slot0(), liquidity(), Swap); or PONS v2's graduation, a Uniswap v4 pool (uniswap-v4-pool: no address; PoolManager 0x8366a39cc670b4001a1121b8f6a443a643e40951; StateView 0xf3334192d15450cdd385c8b70e03f9a6bd9e673b; key from the pool's Initialize: native ETH or the pair token as currency0, CURB the other, fee 0, tick spacing 200, hook 0xE5e702641Ea86F4ae6cC3cDaeD2B886f976Be044). Creation block from the chain: the factory's PairCreated / PoolCreated event, the pool's first explorer transaction, or for v4 the PoolManager's Initialize, beside the venue's PoolGraduated in the same transactionWrite priceSource into the record. For the decided pool (CURB / native ETH on Uniswap v4, the venue's graduation; 17 September 2026) it is what node scripts/pons-pool.ts <token> prints after reading the pool three ways: { "kind": "uniswap-v4-pool", "poolManager": "0x8366a39cc670b4001a1121b8f6a443a643e40951", "stateView": "0xf3334192d15450cdd385c8b70e03f9a6bd9e673b", "key": { "currency0": "0x0000000000000000000000000000000000000000", "currency1": "<CURB>", "fee": 0, "tickSpacing": 200, "hooks": "0xe5e702641ea86f4ae6cc3cdaed2b886f976be044" }, "poolId": "<derived>", "fromBlock": <the Initialize block>, "quote": { "kind": "chainlink-feed", "feed": "0x78F3556b67E17Df817D51Ef5a990cDaF09E8d3A9" } } (ETH / USD; a native-ETH quote is never taken as dollars). The reader also knows a constant-product pair (uniswap-v2-pair, "quote": { "kind": "usd-stable" } for a dollar pair such as USDG 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168) and a v3 pool, should the record change. The reviewer signs again. Then node scripts/deploy-credit-desk.ts records/credit-desk.4663.json --dry-run --reviewed checks the pool, sending nothing; it notes the evidence file exists and a real run would be refused, which is the point. It checks fromBlock too, refusing any pool log before it (paged on a width-capped endpoint, saying how far). The site repeats the check once per pool on its first tick with the pool, and holds the index while it disagrees. Two checks are the reviewer's alone: that kind is the pool's real interface (a v2 pair and a v3 pool both answer token0(); a wrong kind surfaces only at the tick, where a pool whose price moved without a Swap the reader recognises is refused as UNREAD, never priced from its first price); and that a usd-stable quote's other side really is USDG (the reader takes whatever it is, at its own decimals, as dollars)The pool has no code; it does not answer token0() and token1(); it holds neither side as the token; a feed does not answer decimals() and latestRoundData(); fromBlock is not a non-negative integer, is past the head, or is later than a log the pool emittedThe dry run passes with pool: … holds the token against …
13OperatorConfigure the poolRow 12Set CURB_CREDITS with the reviewed priceSource and deploy it. Get a fresh successful tick; rate, config and code identities must agree before row 11Invalid configuration, absent/currently unread or stale code/rate, wrong identity, insufficient price history or liquidity holds payment readinessDated quote, source and buffer limits on /api/credits and /services; a rate alone is not a successful payment. Older top-ups reconciled by their recorded pricing basis
14Product ownerDecide public invitations and paid betaRow 11's end-to-end result; current readiness; reviewed terms; assigned operations; budget/capacity; recorded scoped decisionsReview the mainnet dossier and readiness evidence; when authorized, publish the actual service scope and limits. Milestones come from observations; a token or paid beta does not pass position gatesMissing required evidence or owner signoff remains HELDPublished invitation/beta decision tied to release and evidence; service milestones shown apart from the position prototype
15OperatorOperate and verify recoveryAssigned primary/backup responders; monitoring; backup schedule; actual operating evidenceThe tick indexes, verifies, prices and delivers on its schedule. Monitor missed runs outside GitHub's failure domain, durable logs, pending payments and delivery backlog. Follow RUNBOOK. Take an off-provider backup on the agreed routine and before migration; prove isolated restore. Record incidents and actual response/recovery timesMissing or unread monitoring is not a healthy status. CreditDesk has no pause: disabling invitations does not stop direct transfersDated health/backup/restore/incident evidence; measured beta outcomes; known gaps visible
What is refused by design, and stays refused
  • —A record nobody reviewed by name; a public chain without --reviewed; a key from anywhere but the operator's shell; a second deployment over an existing evidence file, which only a person moves aside (rows 5–7).
  • —An address copied from a documentation example. Every address comes from the chain via a tool, or the explorer via the named reviewer.
  • —Any edit to contracts/src/CreditDesk.sol or the compiler settings after deployment. CI refuses it until the build record is re-recorded (check:build: the record is not this source's bytecode). Re-recording turns a correct desk into a MISMATCH on /services and /api/state. A change is a new desk, record and deployment.
  • —A typed-in price (the conversion). Until row 13 nothing is quoted; a top-up meanwhile is indexed and waits.
  • —A promise the code does not keep: the eight claims refused for the position are refused for the token (the token record). The services page says what the desk does, not what a launch might.
What this checklist does not do

It decides nothing. The signers (A4), the reviewer (A2), the launch and the pool are people's decisions, recorded when made. It does not launch the token or choose where. It does not cover the position product's own deployment (rows 1–6 of the deployment plan), which waits on its own gate decision. It does not hold, make or ask for a key.