{
  "platform": "AgentOnly",
  "version": 2,
  "generatedAt": "2026-10-23T20:25:52.522Z",
  "generatedFrom": [
    "rule ids/names/implemented set: launcher/browser-src/rules.generated.mjs (generated from engine/src/lib.rs)",
    "names/descriptions/status/docs: the v1 registry (canonical docs wording + rulebook verification labels, reconciled with launch records)",
    "parameters: launcher/gen-hooks-registry.mjs PARAM_SCHEMA, transcribed from encodeParams in launch.mjs and proven against it by launcher/test-hooks-registry.mjs",
    "errors: the EngineError enum in engine/src/lib.rs",
    "compatibility: validateSpec (launch.mjs) + the composition matrix in aonly-site/hooks/docs",
    "examples: deploy/launch-record-*.json spec entries for live-carried rules; the skill configuration reference otherwise",
    "rule 18: SPEC-BUY-COOLDOWN.md (spec only)"
  ],
  "description": "Versioned machine-readable registry of the AgentOnly hook rules. v2 keeps every v1 field and adds parameters, effects, compatibility, error codes, account requirements, and grounded examples per rule.",
  "count": 18,
  "catalogRules": 17,
  "programs": {
    "engine": "EPdYVfsX7Lk9gtLp8CVouiFrNwtxP5g11NR6DDgs6akT",
    "registry": "GftZenfbCBwiTKqui3D67mkgofB5wkgAyzRxBEPiwL2e",
    "distributor": "DpRjnQiywdJTUSWBvsJ1vj2mdvvCEDFzAStZJtL1tCQa",
    "crown": "HWZpJ7GnDifwo7g77PKefeVXdzbse7sfRjKrNUna3gJN",
    "feeSchedule": "66HUbm4tgcCsRkDL655UUrmm1aEVGugJzcYNm6NDFkFL",
    "houseSwap": "8aeCfKabRfbXrLY68CXbsgp3x7mgikjwgMF3d7oY1Y8h"
  },
  "globalConstraints": [
    {
      "rule": "at most 8 rules per token",
      "enforcedByValidation": true,
      "source": "validateSpec + engine MAX_RULES"
    },
    {
      "rule": "each rule at most once per stack — so exactly one Trading Hours window per token through this launcher",
      "enforcedByValidation": true,
      "source": "validateSpec (the engine itself would store duplicates)"
    },
    {
      "rule": "known, implemented rules only — a config may never claim a rule its Execute would skip",
      "enforcedByValidation": true,
      "source": "validateSpec + engine config-write validation"
    },
    {
      "rule": "rules 5 + 6 + 15 settlement carves (crownBps + titheBps + titheBps, defaults included) must sum to ≤ 10000 bps",
      "enforcedByValidation": true,
      "source": "validateSpec, mirrored from the engine split_sum check; the Distributor re-checks at settlement (SplitBpsTooLarge)"
    },
    {
      "rule": "rule 14 (Decay Tax) is rejected in any stack on this launcher",
      "enforcedByValidation": true,
      "source": "validateSpec — DBC mints cannot carry the Transfer Fee extension"
    },
    {
      "rule": "rules evaluate in stack order on every transfer; the first veto ends the transfer atomically",
      "enforcedByValidation": false,
      "source": "engine Execute semantics (rulebook composition matrix)"
    }
  ],
  "baseExtraAccounts": [
    "config (the token’s engine Config PDA)",
    "instructions sysvar"
  ],
  "genericErrors": [
    {
      "code": 3,
      "name": "BadCaller",
      "when": "Execute not invoked from a genuine transfer context",
      "remediation": "use a hook-aware swap/transfer builder that resolves the meta list"
    },
    {
      "code": 4,
      "name": "BadConfig",
      "when": "config missing, corrupt, or foreign",
      "remediation": "verify the mint and its engine Config PDA"
    },
    {
      "code": 7,
      "name": "UnknownRule",
      "when": "rule id outside the registry (or unimplemented in this engine build)",
      "remediation": "the config claims a rule the engine cannot enforce — do not trade against it"
    },
    {
      "code": 9,
      "name": "HolderMissing",
      "when": "required holder state absent where creation is impossible",
      "remediation": "sync/register through hook.mjs so the record can be created"
    },
    {
      "code": 10,
      "name": "BadState",
      "when": "state account foreign-owned or corrupt",
      "remediation": "do not retry blindly; inspect the account owner"
    },
    {
      "code": 13,
      "name": "RentVaultUnderfunded",
      "when": "lazy holder creation cannot be paid for",
      "remediation": "the launcher tops up that token’s rent vault"
    },
    {
      "code": 26,
      "name": "MissingExtra",
      "when": "a required extra account (per the config’s templated set) was not supplied",
      "remediation": "refresh the meta list (engine tag 9) or use a builder that resolves the published list"
    }
  ],
  "rules": [
    {
      "id": 1,
      "slug": "basis-latch",
      "name": "Basis Latch",
      "description": "Your average entry price is written down when you buy; while the price is at or above your basis, transfers out are vetoed — you can't sell into your own profit. The TaxHarvest rule.",
      "status": "live-proven",
      "docs": "https://agentonly.fun/docs/#rule-1",
      "category": "price-and-exit",
      "availability": "standard-launch",
      "implementedInEngine": true,
      "enforcement": "engine (transfer hook) + engine price feed",
      "parameters": [
        {
          "name": "mode",
          "type": "enum",
          "values": [
            "latch",
            "live",
            "graduated"
          ],
          "default": "latch",
          "note": "latch: first dip below basis unlocks forever; live: re-locks on recovery; graduated: sells capped at the earned fraction"
        },
        {
          "name": "maxStaleSecs",
          "type": "u64",
          "unit": "seconds",
          "default": 0,
          "note": "price-feed staleness limit; exits fail closed past it (StalePrice)"
        },
        {
          "name": "emaHalfLifeSecs",
          "type": "u64",
          "unit": "seconds",
          "default": 0,
          "note": "EMA half-life for the basis feed; 0 takes the engine default path"
        },
        {
          "name": "maxCrankBps",
          "type": "u16",
          "unit": "bps",
          "default": 0,
          "note": "per-crank EMA clamp"
        },
        {
          "name": "baseFracBps",
          "type": "integer",
          "min": 0,
          "max": 10000,
          "unit": "bps",
          "default": 0,
          "note": "0 = no base fraction"
        },
        {
          "name": "vestingSecs",
          "type": "u64",
          "unit": "seconds",
          "default": 0,
          "note": "graduated-mode vesting; 0 = engine default 432000"
        },
        {
          "name": "dipMultBps",
          "type": "integer",
          "min": 0,
          "max": 65535,
          "unit": "bps",
          "default": 0,
          "note": "0 = engine default 20000"
        }
      ],
      "effects": {
        "buy": "Records and blends the buyer’s basis (average entry) from the cranked price feed.",
        "sell": "Vetoed while the feed price is at or above the seller’s basis; latch mode unlocks permanently on the first dip below basis, live mode re-locks on recovery, graduated mode caps the sell at the earned fraction.",
        "transfer": "Wallet-to-wallet sends are judged as exits for the sender.",
        "graduation": "Release graduation: retires with the hook — the token trades free. Never/house: keeps enforcing on every transfer."
      },
      "compatibility": {
        "requires": [],
        "requiresGraduation": null,
        "forbiddenWith": [],
        "conditions": [],
        "notes": [
          "Combines with everything. Required by rule 2. Shares the holder record layout with rule 13. On DBC the price feed is cranked from pool state (CrankPriceDbc); the first crank must land before the first buy and a stale feed fails closed — validateSpec warns."
        ]
      },
      "errors": [
        {
          "code": 15,
          "name": "Locked",
          "when": "rule 1 veto: price at or above the sender’s basis, not latched",
          "remediation": "wait for a dip below basis (latch mode) or check status before selling"
        },
        {
          "code": 16,
          "name": "StalePrice",
          "when": "rule 1 fail-closed: price feed older than maxStaleSecs",
          "remediation": "anyone may crank the feed (hook.mjs crank-price / CrankPriceDbc), then retry"
        },
        {
          "code": 17,
          "name": "BasisMissing",
          "when": "rule 1 fail-closed: sender holds but has no basis record",
          "remediation": "run hook.mjs migrate / merge to repair holder records"
        },
        {
          "code": 18,
          "name": "PriceFeedBad",
          "when": "rule 1/crank fail-closed: feed missing, corrupt, or for the wrong pool",
          "remediation": "the launcher must initialize/repair the price feed"
        },
        {
          "code": 25,
          "name": "PartialLocked",
          "when": "rule 1 graduated-mode veto: sell exceeds the earned cap",
          "remediation": "sell within the earned fraction, or merge holder records"
        }
      ],
      "extraAccountsBeyondBase": [
        "src holder",
        "dst holder",
        "rent vault",
        "price feed",
        "system program"
      ],
      "example": {
        "entry": {
          "emaHalfLifeSecs": 0,
          "id": 1,
          "maxCrankBps": 0,
          "maxStaleSecs": 300,
          "mode": "latch"
        },
        "source": "live launch — EVER (6qfXuiKUsQEutT5nRK9TD8oadCywdqh5jNAtErv3Ghea) launch record spec",
        "mint": "6qfXuiKUsQEutT5nRK9TD8oadCywdqh5jNAtErv3Ghea"
      }
    },
    {
      "id": 2,
      "slug": "escrow-split",
      "name": "Escrow Split",
      "description": "A fixed share of every buy through the token's house venue is held back into a personal escrow instead of trading, and resolves when you sell through the house. Venue-only: a transfer hook can veto, it can't skim a swap. Requires rule 1.",
      "status": "live-proven",
      "docs": "https://agentonly.fun/docs/#rule-2",
      "category": "settlement-and-fees",
      "availability": "standard-launch",
      "implementedInEngine": true,
      "enforcement": "house venue (HouseSwap program) — the engine treats rule 2 as a no-op",
      "parameters": [
        {
          "name": "splitBps",
          "type": "integer",
          "min": 1,
          "max": 10000,
          "unit": "bps",
          "default": 5000,
          "note": "share of a house-venue buy held into the buyer’s personal escrow"
        }
      ],
      "effects": {
        "buy": "On house-venue buys, splitBps of the buy is held into the buyer’s personal escrow instead of trading.",
        "sell": "On house-venue sells, the escrow resolves using rule 1’s basis/dip logic.",
        "transfer": "No effect on wallet-to-wallet transfers.",
        "graduation": "House-only rule: it lives on the house venue the token graduates onto. Curve trades are never split."
      },
      "compatibility": {
        "requires": [
          1
        ],
        "requiresGraduation": "house",
        "forbiddenWith": [
          {
            "rule": 7,
            "mode": "p2p-only",
            "enforcedByValidation": false,
            "reason": "mechanics defeat, documented in the rulebook and NOT caught by validation today: p2p-only vetoes venue transfers and every house-venue trade is one, so the escrow venue could never trade. Do not stack them."
          }
        ],
        "conditions": [],
        "notes": [
          "The engine independently refuses a rule-2 config carrying no house commitment block."
        ]
      },
      "errors": [],
      "extraAccountsBeyondBase": [],
      "example": {
        "entry": {
          "id": 2,
          "splitBps": 5000
        },
        "source": "live launch — HPROOF (5hXj8x3UCx4VqxpoyrNQTbfQxp4MAqjzTj3xqbJDSZEn) launch record spec",
        "mint": "5hXj8x3UCx4VqxpoyrNQTbfQxp4MAqjzTj3xqbJDSZEn"
      }
    },
    {
      "id": 3,
      "slug": "agent-only",
      "name": "Agent-Only",
      "description": "Only wallets with an active record in the Registry may receive the token — buys and sends alike.",
      "status": "live-proven",
      "docs": "https://agentonly.fun/docs/#rule-3",
      "category": "access",
      "availability": "standard-launch",
      "implementedInEngine": true,
      "enforcement": "engine (transfer hook)",
      "parameters": [],
      "effects": {
        "buy": "The receiving wallet must hold an active Registry record, mirrored for this token (sync) — otherwise the receive is vetoed (NotRegistered).",
        "sell": "Judged on the receive side, like every rule here; the seller’s own exit is not gated.",
        "transfer": "Sends to unregistered wallets are vetoed.",
        "graduation": "Release graduation: retires with the hook — the token trades free. Never/house: keeps enforcing on every transfer."
      },
      "compatibility": {
        "requires": [],
        "requiresGraduation": null,
        "forbiddenWith": [],
        "conditions": [],
        "notes": [
          "Combines with everything. Alongside rule 17 it is redundant on receives (17 already refuses the unregistered) but harmless — both read the same holder mirror. With holder fees on, the launcher registers the Distributor vault’s holder mirror at launch (a PDA cannot self-register)."
        ]
      },
      "errors": [
        {
          "code": 1,
          "name": "NotRegistered",
          "when": "rule 3 veto: destination owner holds no active Registry record mirrored for this token",
          "remediation": "register (hook.mjs register), then sync (hook.mjs sync), then retry"
        }
      ],
      "extraAccountsBeyondBase": [
        "dst holder",
        "rent vault",
        "system program"
      ],
      "example": {
        "entry": {
          "id": 3
        },
        "source": "live launch — AGTR (Hfk7uqxZ3fFB4TpAzSKYGCJ3uuAJ1E1fk6i6vSn3FVXR) launch record spec",
        "mint": "Hfk7uqxZ3fFB4TpAzSKYGCJ3uuAJ1E1fk6i6vSn3FVXR"
      }
    },
    {
      "id": 4,
      "slug": "app-gated",
      "name": "App-Gated",
      "description": "Venue trades may only arrive through named apps; the hook checks which program is actually executing the transaction.",
      "status": "trial",
      "docs": "https://agentonly.fun/docs/#rule-4",
      "category": "access",
      "availability": "standard-launch",
      "implementedInEngine": true,
      "enforcement": "engine (transfer hook)",
      "parameters": [
        {
          "name": "apps",
          "type": "pubkey[]",
          "required": true,
          "note": "1–2 app program ids (or app1 / app2 fields); at least one, none may be the zero pubkey; each app must also be in the config’s venue allowlist"
        }
      ],
      "effects": {
        "buy": "Venue receives pass only when the program actually executing the transaction is one of the listed apps (AppNotAllowed).",
        "sell": "Venue receives on the sell path are judged the same way.",
        "transfer": "Wallet-to-wallet transfers are ungated.",
        "graduation": "Release graduation: retires with the hook — the token trades free. Never/house: keeps enforcing on every transfer."
      },
      "compatibility": {
        "requires": [],
        "requiresGraduation": null,
        "forbiddenWith": [],
        "conditions": [],
        "notes": [
          "Its apps must be in the config’s venue allowlist: caller validation runs before any rule, so an unlisted app never gets a trade for rule 4 to judge. The launcher writes the DBC program as the venue — list it as an app if plain curve trades should pass. Moot alongside rule 7 p2p-only."
        ]
      },
      "errors": [
        {
          "code": 32,
          "name": "AppNotAllowed",
          "when": "rule 4 veto: venue receive via a program not in the rule’s app list",
          "remediation": "route the trade through one of the listed apps"
        }
      ],
      "extraAccountsBeyondBase": [],
      "example": {
        "entry": {
          "apps": [
            "dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN"
          ],
          "id": 4
        },
        "source": "live launch — APROOF (9TCCJ6ixgCAc6E26vRa1HGEyspecMUHcTR2dLfMB1AS2) launch record spec",
        "mint": "9TCCJ6ixgCAc6E26vRa1HGEyspecMUHcTR2dLfMB1AS2"
      }
    },
    {
      "id": 5,
      "slug": "crown-beneficiary",
      "name": "Crown Beneficiary",
      "description": "A fixed share of every fee settlement is carved out and paid to whoever currently holds the token's crown — a per-token beneficiary seat.",
      "status": "trial",
      "docs": "https://agentonly.fun/docs/#rule-5",
      "category": "settlement-and-fees",
      "availability": "standard-launch",
      "implementedInEngine": true,
      "enforcement": "distributor settlement (read from the engine config at distribution time)",
      "parameters": [
        {
          "name": "crownBps",
          "type": "integer",
          "min": 1,
          "max": 10000,
          "unit": "bps",
          "default": 500
        }
      ],
      "effects": {
        "buy": "No transfer effect.",
        "sell": "No transfer effect.",
        "transfer": "No transfer effect.",
        "settlement": "crownBps of each Distributor settlement is carved to the Crown program’s current beneficiary for this mint; the leg fails open (share folds into the holder index) until the token’s Crown Init exists.",
        "graduation": "Settlements keep working after release graduation — the Distributor never depended on the hook."
      },
      "compatibility": {
        "requires": [],
        "requiresGraduation": null,
        "forbiddenWith": [],
        "conditions": [
          "Inert without holder fees (no Distributor = no settlements = the carve never pays).",
          "Inert until the token’s one-time Crown Init creates its crown record; the leg fails open and folds into the holder index."
        ],
        "notes": [
          "Settlement legs carve in crown → seat → king order across rules 5, 6, 15, under the shared 10000 bps cap."
        ]
      },
      "errors": [],
      "extraAccountsBeyondBase": [],
      "example": {
        "entry": {
          "id": 5,
          "crownBps": 500
        },
        "source": "skill configuration reference (no live token carries this rule yet)"
      }
    },
    {
      "id": 6,
      "slug": "tithe-seat",
      "name": "Tithe Seat",
      "description": "A fixed share of every fee settlement is paid to whoever holds one specific NFT, named at launch.",
      "status": "trial",
      "docs": "https://agentonly.fun/docs/#rule-6",
      "category": "settlement-and-fees",
      "availability": "standard-launch",
      "implementedInEngine": true,
      "enforcement": "distributor settlement",
      "parameters": [
        {
          "name": "seatMint",
          "type": "pubkey",
          "required": true,
          "note": "the seat NFT mint; its current holder receives the tithe"
        },
        {
          "name": "titheBps",
          "type": "integer",
          "min": 1,
          "max": 10000,
          "unit": "bps",
          "default": 300
        }
      ],
      "effects": {
        "buy": "No transfer effect.",
        "sell": "No transfer effect.",
        "transfer": "No transfer effect.",
        "settlement": "titheBps of each settlement is paid to the seat NFT’s current holder; the leg fails open when no valid seat position exists.",
        "graduation": "Settlements keep working after release graduation."
      },
      "compatibility": {
        "requires": [],
        "requiresGraduation": null,
        "forbiddenWith": [],
        "conditions": [
          "Inert without holder fees.",
          "The leg additionally needs a valid seat position (an account holding exactly 1 of the seatMint) at settlement time; otherwise it skips and folds."
        ],
        "notes": [
          "Shared 10000 bps settlement cap with rules 5 and 15."
        ]
      },
      "errors": [],
      "extraAccountsBeyondBase": [],
      "example": {
        "entry": {
          "id": 6,
          "seatMint": "<seat NFT mint>",
          "titheBps": 300
        },
        "source": "skill configuration reference (no live token carries this rule yet)"
      }
    },
    {
      "id": 7,
      "slug": "p2p-toggle",
      "name": "P2P Toggle",
      "description": "No-p2p mode vetoes wallet-to-wallet sends so every transfer is a venue trade; p2p-only mode does the reverse.",
      "status": "trial",
      "docs": "https://agentonly.fun/docs/#rule-7",
      "category": "transfer-policy",
      "availability": "standard-launch",
      "implementedInEngine": true,
      "enforcement": "engine (transfer hook)",
      "parameters": [
        {
          "name": "mode",
          "type": "enum",
          "values": [
            "p2p-only",
            "no-p2p"
          ],
          "required": true
        }
      ],
      "effects": {
        "buy": "no-p2p: venue buys are the only buys. p2p-only: venue receives are vetoed (VenueTransferForbidden) — the curve can be seeded but not bought from.",
        "sell": "no-p2p: venue sells pass. p2p-only: venue sells are venue transfers and are vetoed.",
        "transfer": "no-p2p: wallet-to-wallet vetoed (P2PTransferForbidden). p2p-only: wallet-to-wallet is the only movement.",
        "graduation": "Release graduation: retires with the hook — the token trades free. Never/house: keeps enforcing on every transfer."
      },
      "compatibility": {
        "requires": [],
        "requiresGraduation": null,
        "forbiddenWith": [],
        "conditions": [],
        "notes": [
          "Combines with everything at validation. Its p2p-only mode has side effects: it moots rule 4, defeats rule 2’s venue, and leaves rule 11 nothing to judge (no venue transfers). validateSpec warns on p2p-only."
        ]
      },
      "errors": [
        {
          "code": 20,
          "name": "VenueTransferForbidden",
          "when": "rule 7 (p2p-only) veto: venue-context transfer",
          "remediation": "this token moves wallet-to-wallet only"
        },
        {
          "code": 21,
          "name": "P2PTransferForbidden",
          "when": "rule 7 (no-p2p) veto: wallet-context transfer",
          "remediation": "this token moves only through its venue"
        }
      ],
      "extraAccountsBeyondBase": [],
      "example": {
        "entry": {
          "id": 7,
          "mode": "p2p-only"
        },
        "source": "skill configuration reference (no live token carries this rule yet)"
      }
    },
    {
      "id": 8,
      "slug": "holder-gated",
      "name": "Holder-Gated",
      "description": "Receiving requires already holding at least a set amount of a different, named gate token, checked at transfer time.",
      "status": "trial",
      "docs": "https://agentonly.fun/docs/#rule-8",
      "category": "access",
      "availability": "standard-launch",
      "implementedInEngine": true,
      "enforcement": "engine (transfer hook), with a client-surface caveat on the curve",
      "parameters": [
        {
          "name": "gateMint",
          "type": "pubkey",
          "required": true
        },
        {
          "name": "minAmountRaw",
          "type": "u64",
          "unit": "raw gate-token units",
          "note": "minimum gate balance, as raw units — or give minAmount + gateDecimals instead"
        },
        {
          "name": "minAmount",
          "type": "number",
          "note": "minimum gate balance in whole tokens; requires gateDecimals"
        },
        {
          "name": "gateDecimals",
          "type": "integer",
          "note": "decimals of the gate token; required with minAmount"
        }
      ],
      "effects": {
        "buy": "Receiving requires the destination wallet to hold at least the gate minimum, synced at transfer time (NotGateHolder). Plain curve buys fail closed in v1 — a curve swap cannot carry the gate sync; buys work through surfaces that prepend SyncGateBalance (the skill’s send, the trade page).",
        "sell": "Judged on the receive side.",
        "transfer": "Sends are judged on the destination’s gate balance, with the sync prepended by hook-aware surfaces.",
        "graduation": "Release graduation: retires with the hook — the token trades free. Never/house: keeps enforcing on every transfer."
      },
      "compatibility": {
        "requires": [],
        "requiresGraduation": null,
        "forbiddenWith": [],
        "conditions": [],
        "notes": [
          "Combines with everything, with the venue caveat in its buy effect: plain curve buys fail closed; validateSpec warns."
        ]
      },
      "errors": [
        {
          "code": 19,
          "name": "NotGateHolder",
          "when": "rule 8 veto: destination gate balance short or absent",
          "remediation": "acquire the gate minimum, and use a surface that prepends SyncGateBalance"
        },
        {
          "code": 23,
          "name": "BadGateAccount",
          "when": "SyncGateBalance presented an account that is not the owner’s gate ATA",
          "remediation": "present the owner’s actual gate token account"
        }
      ],
      "extraAccountsBeyondBase": [
        "dst holder"
      ],
      "example": {
        "entry": {
          "id": 8,
          "gateMint": "Hfk7uqxZ3fFB4TpAzSKYGCJ3uuAJ1E1fk6i6vSn3FVXR",
          "minAmount": 1000,
          "gateDecimals": 6
        },
        "source": "skill gate worked example (the SQ2 spec; no live token carries this rule yet)"
      }
    },
    {
      "id": 9,
      "slug": "max-wallet",
      "name": "Max Wallet",
      "description": "No wallet may receive past a cap share of total supply, set in basis points and checked at receive time.",
      "status": "live-proven",
      "docs": "https://agentonly.fun/docs/#rule-9",
      "category": "limits",
      "availability": "standard-launch",
      "implementedInEngine": true,
      "enforcement": "engine (transfer hook)",
      "parameters": [
        {
          "name": "capBps",
          "type": "integer",
          "min": 1,
          "max": 10000,
          "unit": "bps",
          "required": true,
          "aliases": [
            "maxBps"
          ],
          "note": "no wallet may receive past this share of total supply"
        }
      ],
      "effects": {
        "buy": "A receive that would push the wallet past capBps of total supply is vetoed outright — not partially filled (MaxWalletExceeded).",
        "sell": "Judged on the receive side; selling out is never capped.",
        "transfer": "Sends are capped at the destination.",
        "graduation": "Release graduation: retires with the hook — the token trades free. Never/house: keeps enforcing on every transfer."
      },
      "compatibility": {
        "requires": [],
        "requiresGraduation": null,
        "forbiddenWith": [],
        "conditions": [],
        "notes": [
          "Combines with everything; no interactions. The pool, distributor, and house vault accounts are exempt by launch config, so the cap never blocks the token’s own plumbing."
        ]
      },
      "errors": [
        {
          "code": 2,
          "name": "MaxWalletExceeded",
          "when": "rule 9 veto: receive would exceed the cap share of supply",
          "remediation": "reduce the receive amount or use a different wallet under the cap"
        }
      ],
      "extraAccountsBeyondBase": [],
      "example": {
        "entry": {
          "capBps": 500,
          "id": 9
        },
        "source": "live launch — AGTR (Hfk7uqxZ3fFB4TpAzSKYGCJ3uuAJ1E1fk6i6vSn3FVXR) launch record spec",
        "mint": "Hfk7uqxZ3fFB4TpAzSKYGCJ3uuAJ1E1fk6i6vSn3FVXR"
      }
    },
    {
      "id": 10,
      "slug": "allow-blocklist",
      "name": "Allow/Blocklist",
      "description": "Curated lists enforced at receive time: the blocklist names wallets that may never receive; the allowlist inverts it — only pre-approved accounts may.",
      "status": "trial",
      "docs": "https://agentonly.fun/docs/#rule-10",
      "category": "access",
      "availability": "standard-launch",
      "implementedInEngine": true,
      "enforcement": "engine (transfer hook); lists live in per-mint PDAs written after launch",
      "parameters": [
        {
          "name": "mode",
          "type": "enum",
          "values": [
            "blocklist",
            "allowlist",
            "both"
          ],
          "default": "both",
          "note": "the lists themselves live in per-mint PDAs written after launch, not in the config params"
        }
      ],
      "effects": {
        "buy": "Receive judged against the lists: blocklisted wallets may never receive (Blocklisted); in allowlist mode only listed wallets may (NotAllowlisted).",
        "sell": "Judged on the receive side.",
        "transfer": "Judged on the destination.",
        "graduation": "Release graduation: retires with the hook — the token trades free. Never/house: keeps enforcing on every transfer."
      },
      "compatibility": {
        "requires": [],
        "requiresGraduation": null,
        "forbiddenWith": [],
        "conditions": [],
        "notes": [
          "Combines with everything; the lists are curated after launch, so there is nothing to conflict at stack time."
        ]
      },
      "errors": [
        {
          "code": 30,
          "name": "NotAllowlisted",
          "when": "rule 10 veto: destination holds no active allowlist entry",
          "remediation": "the list curator must add the wallet after launch"
        },
        {
          "code": 31,
          "name": "Blocklisted",
          "when": "rule 10 veto: destination owner is on the sealed blocklist",
          "remediation": "no remediation — the wallet may never receive this token"
        }
      ],
      "extraAccountsBeyondBase": [],
      "example": {
        "entry": {
          "id": 10,
          "mode": "both"
        },
        "source": "skill configuration reference (no live token carries this rule yet)"
      }
    },
    {
      "id": 11,
      "slug": "the-grudge",
      "name": "The Grudge",
      "description": "The token remembers who dumped it. Sell more than a set fraction of your bag inside a set window and you're marked — while the mark lasts, venue buys are banned.",
      "status": "trial",
      "docs": "https://agentonly.fun/docs/#rule-11",
      "category": "price-and-exit",
      "availability": "standard-launch",
      "implementedInEngine": true,
      "enforcement": "engine (transfer hook)",
      "parameters": [
        {
          "name": "dumpThresholdBps",
          "type": "integer",
          "min": 1,
          "max": 10000,
          "unit": "bps",
          "default": 5000,
          "note": "selling more than this share of the bag inside the window sets the mark"
        },
        {
          "name": "windowSecs",
          "type": "u64",
          "unit": "seconds",
          "default": 0,
          "note": "0 = engine default 86400"
        },
        {
          "name": "penaltyDurationSecs",
          "type": "u64",
          "unit": "seconds",
          "default": 0,
          "note": "0 = engine default 86400"
        },
        {
          "name": "penaltyMode",
          "type": "enum",
          "values": [
            "ban",
            "cap"
          ],
          "default": "ban"
        },
        {
          "name": "buyCapAmount",
          "type": "u64",
          "unit": "raw token units",
          "note": "required when penaltyMode is \"cap\""
        },
        {
          "name": "maxEscalation",
          "type": "integer",
          "min": 0,
          "max": 255,
          "default": 0,
          "note": "0 = engine default 4"
        }
      ],
      "effects": {
        "buy": "A marked wallet’s venue buys are banned, or capped at buyCapAmount in cap mode (GrudgeMarked).",
        "sell": "Selling more than dumpThresholdBps of the bag inside windowSecs sets the mark.",
        "transfer": "Judges venue transfers only; wallet-to-wallet movement is not judged and never marks.",
        "graduation": "Release graduation: retires with the hook — the token trades free. Never/house: keeps enforcing on every transfer."
      },
      "compatibility": {
        "requires": [],
        "requiresGraduation": null,
        "forbiddenWith": [],
        "conditions": [],
        "notes": [
          "Combines with everything. Judges venue transfers only, so under rule 7 p2p-only it never marks anyone."
        ]
      },
      "errors": [
        {
          "code": 27,
          "name": "GrudgeMarked",
          "when": "rule 11 veto: wallet is marked (buy-ban, or buy over the marked cap)",
          "remediation": "wait out the penalty duration (it escalates on repeat dumps)"
        }
      ],
      "extraAccountsBeyondBase": [],
      "example": {
        "entry": {
          "id": 11,
          "dumpThresholdBps": 5000,
          "penaltyMode": "ban"
        },
        "source": "skill configuration reference (no live token carries this rule yet)"
      }
    },
    {
      "id": 12,
      "slug": "trading-hours",
      "name": "Trading Hours",
      "description": "Transfers are allowed only inside one daily window on chosen weekdays, in one fixed timezone. Outside the window, the hook vetoes.",
      "status": "live-proven",
      "docs": "https://agentonly.fun/docs/#rule-12",
      "category": "time",
      "availability": "standard-launch",
      "implementedInEngine": true,
      "enforcement": "engine (transfer hook)",
      "parameters": [
        {
          "name": "tzOffsetSecs",
          "type": "integer",
          "min": -43200,
          "max": 50400,
          "unit": "seconds",
          "default": -18000,
          "note": "fixed UTC offset — no DST, by design"
        },
        {
          "name": "daysMask",
          "type": "integer",
          "min": 1,
          "max": 127,
          "default": 31,
          "note": "bit0 = Mon … bit6 = Sun; at least one day"
        },
        {
          "name": "windowStartMin",
          "type": "integer",
          "min": 0,
          "max": 1439,
          "unit": "minutes after local midnight",
          "default": 540
        },
        {
          "name": "windowEndMin",
          "type": "integer",
          "min": 1,
          "max": 1440,
          "unit": "minutes after local midnight",
          "default": 1020,
          "note": "must be > windowStartMin"
        },
        {
          "name": "scope",
          "type": "integer",
          "min": 0,
          "max": 1,
          "default": 0,
          "note": "0 = all transfers, 1 = venue transfers only"
        }
      ],
      "effects": {
        "buy": "Vetoed outside the daily window on masked weekdays (OutsideTradingHours), unless scope limits judging to venue transfers.",
        "sell": "Same window judgement.",
        "transfer": "Same window judgement at scope 0; exempt at scope 1.",
        "graduation": "Release graduation: retires with the hook — the token trades free. Never/house: keeps enforcing on every transfer."
      },
      "compatibility": {
        "requires": [],
        "requiresGraduation": null,
        "forbiddenWith": [],
        "conditions": [],
        "notes": [
          "Combines with everything. Exactly one entry per token through the launcher (duplicate ids are rejected), so split days are not expressible."
        ]
      },
      "errors": [
        {
          "code": 28,
          "name": "OutsideTradingHours",
          "when": "rule 12 veto: transfer outside the configured window",
          "remediation": "trade inside the configured window (fixed UTC offset, no DST)"
        }
      ],
      "extraAccountsBeyondBase": [],
      "example": {
        "entry": {
          "daysMask": 127,
          "id": 12,
          "scope": 0,
          "tzOffsetSecs": 0,
          "windowEndMin": 878,
          "windowStartMin": 833
        },
        "source": "live launch — HRPROOF (AT8Gr9xPcVCWTrCSo8ZnJiEv2jEQ26oxwQcL2762eu2h) launch record spec",
        "mint": "AT8Gr9xPcVCWTrCSo8ZnJiEv2jEQ26oxwQcL2762eu2h"
      }
    },
    {
      "id": 13,
      "slug": "vesting-clock",
      "name": "Vesting Clock",
      "description": "Tokens unlock on a schedule. Each holder's anchor is the balance-weighted average time they received their position; only the unlocked fraction can move.",
      "status": "trial",
      "docs": "https://agentonly.fun/docs/#rule-13",
      "category": "time",
      "availability": "standard-launch",
      "implementedInEngine": true,
      "enforcement": "engine (transfer hook)",
      "parameters": [
        {
          "name": "mode",
          "type": "enum",
          "values": [
            "cliff",
            "linear"
          ],
          "default": "cliff"
        },
        {
          "name": "holdSecs",
          "type": "u64",
          "unit": "seconds",
          "default": 0,
          "note": "per-holder hold; at least one of holdSecs / releaseTs must be set"
        },
        {
          "name": "releaseTs",
          "type": "i64",
          "unit": "unix timestamp",
          "default": 0,
          "note": "global release time; at least one of holdSecs / releaseTs must be set"
        }
      ],
      "effects": {
        "buy": "Receives set and blend the holder’s vesting anchor (balance-weighted average receive time).",
        "sell": "Outbound movement above the unlocked fraction is vetoed (VestingLocked). “Released” lifts the veto — tokens never move by themselves.",
        "transfer": "Judged like a sell for the sender.",
        "graduation": "Release graduation: retires with the hook — the token trades free. Never/house: keeps enforcing on every transfer."
      },
      "compatibility": {
        "requires": [],
        "requiresGraduation": null,
        "forbiddenWith": [],
        "conditions": [],
        "notes": [
          "Shares the holder record layout with rule 1 (graduated fields and the vesting anchor live in one account)."
        ]
      },
      "errors": [
        {
          "code": 29,
          "name": "VestingLocked",
          "when": "rule 13 veto: outbound amount exceeds the unlocked fraction",
          "remediation": "wait for the schedule to unlock more, or move only the unlocked fraction"
        }
      ],
      "extraAccountsBeyondBase": [],
      "example": {
        "entry": {
          "id": 13,
          "mode": "cliff",
          "holdSecs": 86400
        },
        "source": "skill configuration reference (no live token carries this rule yet)"
      }
    },
    {
      "id": 14,
      "slug": "decay-tax",
      "name": "Decay Tax",
      "description": "Not launchable today. It works by modulating a mint's Token-2022 transfer fee, which the DBC launch path doesn't carry — it needs a direct-mint build. Documented, not shipped. We say so on the label; the rulebook explains why.",
      "status": "not-launchable",
      "docs": "https://agentonly.fun/docs/#rule-14",
      "category": "settlement-and-fees",
      "availability": "unavailable-standard-launch",
      "implementedInEngine": true,
      "enforcement": "FeeSchedule program (fee-extension mints only) — NOT the DBC launch path",
      "parameters": [
        {
          "name": "startBps",
          "type": "integer",
          "min": 1,
          "max": 10000,
          "unit": "bps",
          "default": 500
        },
        {
          "name": "floorBps",
          "type": "integer",
          "min": 0,
          "note": "must be ≤ startBps",
          "default": 100
        },
        {
          "name": "decaySecs",
          "type": "u64",
          "unit": "seconds",
          "default": 2592000,
          "note": "must be positive; the rate only ever moves down"
        }
      ],
      "effects": {
        "buy": "No transfer-hook effect; the rule modulates the mint’s Token-2022 transfer-fee rate.",
        "sell": "Same schedule.",
        "transfer": "The mint-wide fee rate decays linearly from startBps to floorBps over decaySecs; the rate only ever moves down, on a permissionless crank.",
        "graduation": "Not launchable through the standard launcher: DBC mints carry a fixed extension set with no Transfer Fee extension, so a rule-14 config could never initialize its schedule (InitSchedule fails closed with NoFeeConfig). A direct fee-extension mint path is the unbuilt unblock."
      },
      "compatibility": {
        "requires": [],
        "requiresGraduation": null,
        "forbiddenWith": [],
        "conditions": [],
        "notes": [
          "Pairs with nothing through the standard launcher — rejected unconditionally at spec time (structural, not merely untested)."
        ]
      },
      "errors": [],
      "extraAccountsBeyondBase": [],
      "example": {
        "entry": {
          "id": 14,
          "startBps": 500,
          "floorBps": 100,
          "decaySecs": 2592000
        },
        "source": "skill configuration reference — shown for shape only; the launcher rejects rule 14 at spec time"
      }
    },
    {
      "id": 15,
      "slug": "king-of-the-pool",
      "name": "King of the Pool",
      "description": "A per-token record names the largest holder — and when a tithe is configured, that share of fee settlements goes to whoever sits on the throne.",
      "status": "trial",
      "docs": "https://agentonly.fun/docs/#rule-15",
      "category": "settlement-and-fees",
      "availability": "standard-launch",
      "implementedInEngine": true,
      "enforcement": "engine (largest-holder tracking) + distributor settlement",
      "parameters": [
        {
          "name": "titheBps",
          "type": "integer",
          "min": 0,
          "max": 10000,
          "unit": "bps",
          "default": 0,
          "note": "0 = tracking only"
        }
      ],
      "effects": {
        "buy": "Transfers update the per-token largest-holder record.",
        "sell": "Same tracking.",
        "transfer": "Same tracking.",
        "settlement": "titheBps of each settlement goes to the current king (0 = tracking only); the leg fails open before any king exists.",
        "graduation": "Tracking retires with the hook on release; settlements keep working after release graduation."
      },
      "compatibility": {
        "requires": [],
        "requiresGraduation": null,
        "forbiddenWith": [],
        "conditions": [
          "The tithe leg is inert without holder fees, and before any king exists; the tracking half runs from the first transfer regardless."
        ],
        "notes": [
          "Shared 10000 bps settlement cap with rules 5 and 6."
        ]
      },
      "errors": [],
      "extraAccountsBeyondBase": [],
      "example": {
        "entry": {
          "id": 15,
          "titheBps": 200
        },
        "source": "skill configuration reference (no live token carries this rule yet)"
      }
    },
    {
      "id": 16,
      "slug": "buyback-and-burn",
      "name": "Buyback & Burn",
      "description": "The token's own trading fees buy the token and burn it, once the claimable SOL crosses a threshold set at launch.",
      "status": "trial",
      "docs": "https://agentonly.fun/docs/#rule-16",
      "category": "settlement-and-fees",
      "availability": "standard-launch",
      "implementedInEngine": true,
      "enforcement": "crank-operated (the token’s fee claimer runs the crank)",
      "parameters": [
        {
          "name": "thresholdSol",
          "type": "number",
          "unit": "SOL",
          "default": 1,
          "note": "claimable-fee threshold that triggers the buyback crank; or thresholdLamports"
        },
        {
          "name": "thresholdLamports",
          "type": "u64",
          "unit": "lamports",
          "note": "alternative to thresholdSol"
        },
        {
          "name": "callerCutBps",
          "type": "integer",
          "min": 0,
          "max": 1000,
          "unit": "bps",
          "default": 100
        }
      ],
      "effects": {
        "buy": "No transfer effect.",
        "sell": "No transfer effect.",
        "transfer": "No transfer effect.",
        "graduation": "Once claimable curve fees cross the threshold, the crank claims them, buys the token on its own curve and burns it. Works alongside holder-fee routing — separate paths over the same accrued fees."
      },
      "compatibility": {
        "requires": [],
        "requiresGraduation": null,
        "forbiddenWith": [],
        "conditions": [],
        "notes": [
          "Can be combined with holder-fee routing — no check in the launcher, engine, or Distributor forbids it; the two are separate paths over the same accrued fees, so which path claims a given accrual is an operational choice."
        ]
      },
      "errors": [],
      "extraAccountsBeyondBase": [],
      "example": {
        "entry": {
          "id": 16,
          "thresholdSol": 1,
          "callerCutBps": 100
        },
        "source": "skill configuration reference (no live token carries this rule yet)"
      }
    },
    {
      "id": 17,
      "slug": "ecosystem-gate",
      "name": "Ecosystem Gate",
      "description": "Receiving requires a Registry record attested through a named ecosystem's own flow — but today only Muse's flow can stamp one: of the 16 presets (Muse, Fomo, Claude, Grok named), only the Muse attester is configured, and presets 2–16 cannot currently attest a wallet. Provenance, not a vibe check.",
      "status": "trial",
      "docs": "https://agentonly.fun/docs/#rule-17",
      "category": "access",
      "availability": "standard-launch",
      "implementedInEngine": true,
      "enforcement": "engine (transfer hook)",
      "parameters": [
        {
          "name": "ecosystem",
          "type": "enum|integer",
          "values": [
            "muse",
            "fomo",
            "claude",
            "grok"
          ],
          "required": true,
          "note": "preset name (case-insensitive) or a number 1–16; aliases like { \"rule\": \"muse-only\" } imply it. Only the Muse attester (id 1) is live today"
        }
      ],
      "effects": {
        "buy": "Receiving requires a Registry registration carrying an attested tag for the named ecosystem (WrongEcosystem); a wallet with no registration at all is vetoed as NotRegistered. The tag proves the wallet was created through that ecosystem’s flow and attested by its attester key — not what software operates it today.",
        "sell": "Judged on the receive side.",
        "transfer": "Sends to wallets without the required attested tag are vetoed.",
        "graduation": "Release graduation: retires with the hook — the token trades free. Never/house: keeps enforcing on every transfer."
      },
      "compatibility": {
        "requires": [],
        "requiresGraduation": null,
        "forbiddenWith": [],
        "conditions": [
          "Naming an ecosystem with no configured attester (presets 2–16 today) launches fine but passes nobody: no wallet can hold the required attestation yet. validateSpec warns."
        ],
        "notes": [
          "Redundant alongside rule 3 on receives, but harmless."
        ]
      },
      "errors": [
        {
          "code": 24,
          "name": "WrongEcosystem",
          "when": "rule 17 veto: destination registered, but not attested for the required ecosystem",
          "remediation": "complete that ecosystem’s attestation flow so the registration carries its tag"
        },
        {
          "code": 1,
          "name": "NotRegistered",
          "when": "rule 17 veto path: destination holds no Registry registration at all",
          "remediation": "register first, then complete the ecosystem attestation"
        }
      ],
      "extraAccountsBeyondBase": [
        "dst holder",
        "rent vault",
        "system program"
      ],
      "example": {
        "entry": {
          "id": 17,
          "ecosystem": "muse"
        },
        "source": "skill configuration reference (no live token carries this rule yet)"
      }
    },
    {
      "id": 18,
      "slug": "buy-cooldown",
      "name": "Buy Cooldown",
      "description": "Spec only (SPEC-BUY-COOLDOWN.md): after a counted buy, the same wallet may not buy again until cooldownSecs elapse. Not implemented in the engine and not launchable — the launcher rejects it like any unimplemented rule.",
      "status": "spec-only",
      "docs": "https://github.com/anondevv69/hook-launcher",
      "category": "transfer-policy",
      "availability": "spec-only",
      "implementedInEngine": false,
      "enforcement": "spec only — no engine implementation exists",
      "parameters": [
        {
          "name": "cooldownSecs",
          "type": "u64",
          "min": 5,
          "max": 86400,
          "unit": "seconds",
          "required": true,
          "note": "SPEC-BUY-COOLDOWN §5; minimum after a counted buy before the same wallet may buy again"
        },
        {
          "name": "exemptCreator",
          "type": "boolean",
          "default": false,
          "note": "per the spec’s example entry"
        }
      ],
      "effects": {
        "buy": "Per SPEC-BUY-COOLDOWN: a counted buy (venue receive) starts a per-wallet cooldown; another counted buy before cooldownSecs elapses would be vetoed (CooldownActive, reserved error 35).",
        "sell": "Never gated, per the spec.",
        "transfer": "Wallet-to-wallet receives never count and are never gated, per the spec (counting them would let anyone grief a victim’s cooldown).",
        "graduation": "The spec’s default is that the cooldown persists after release graduation."
      },
      "compatibility": {
        "requires": [],
        "requiresGraduation": null,
        "forbiddenWith": [],
        "conditions": [],
        "notes": [
          "Spec-only. SPEC-BUY-COOLDOWN designs it to stack on rules 3/17 (attestation is what makes a per-wallet cooldown costly to evade); the spec’s own sybil analysis says a cooldown alone binds a wallet, not an identity."
        ]
      },
      "errors": [
        {
          "code": 35,
          "name": "CooldownActive",
          "when": "RESERVED by SPEC-BUY-COOLDOWN — not assigned in the deployed engine; a buy inside the cooldown window would veto with this code once rule 18 ships",
          "remediation": "wait out the cooldown"
        }
      ],
      "extraAccountsBeyondBase": [],
      "example": {
        "entry": {
          "id": 18,
          "cooldownSecs": 300,
          "exemptCreator": false
        },
        "source": "SPEC-BUY-COOLDOWN §5 example entry — spec only, not launchable"
      }
    }
  ]
}
