# OSRS Codex — Entry Schema (v1) The codex is a **client-agnostic** knowledge base. Every bot-able OSRS activity is described here once, in a strict YAML structure. AI agents do **not** read the game — they read a codex entry and *translate* its abstract actions into whatever client API they target (DreamBot, Microbot, OSBot, RuneLite plugins, custom, ...). Two invariants make translation reliable: 1. **One entry = one bot-able activity**, described end-to-end. 2. **Every action references the universal vocabulary** (`docs/action-grammar.md`). No client-specific calls ever appear in an entry. --- ## File layout ``` osrs-codex/ docs/ schema.md <- this file action-grammar.md <- universal action vocabulary data-sources.md <- where facts come from + verification rules codex/ economy/ <- money-making: flips, alching, drops, selling training/ <- level training per skill (per band) combat/ <- bossing, skilling-in-combat combat-slayers/ <- slayer-specific quests/ <- questable XP / requirement unlocks transport/ <- movement, teleporting, banking logistics meta/ <- account building, mule/logistics, world setup schemas/ entry.schema.json <- JSON-Schema for validation tools/ validate.py <- runs the schema over all codex/**.yaml INDEX.md <- machine-generated catalog of every entry ``` An activity that spans domains (e.g. alchemy training *and* money) lives in its **primary** domain; cross-refs go in `related`. --- ## Entry structure (YAML) ```yaml id: alching-nature-runecoins # kebab-case, globally unique title: Low-Level Alchemy (F2P) domain: economy # one of the codex/ dirs tags: [alching, money, f2p, p2p-safe] status: verified # draft | verified | stale version: 3 updated: 2026-08-27 # last date facts were re-verified summary: > One-sentence description of what the bot does and why it runs. # ---------------------------------------------------------------- accounts accounts: membership: f2p # f2p | members | hybrid (which items) min_level: 1 # absolute floor to start requires_quest: [] # quest ids (see codex/quests/) requires_other: # free-form hard prerequisites - "A bankable inventory + bank access" # ---------------------------------------------------------------- setup best_practice: # REQUIRED — best-in-class execution equipment: - item: staff of fire why: "unlimited fire runes; drops LA rune cost to 1 nature/cast" source: "OSRS wiki — Staff of Fire; Low Level Alchemy (Cost)" techniques: - name: cast on noted (stacked) items why: "one slot, minimal mouse travel" source: "OSRS wiki — Low Level Alchemy (Usage)" # ---------------------------------------------------------------- economy economy: cost_per_cycle: 62 # GP consumed per meaningful action income_per_cycle: 81 net_per_cycle: 19 gp_per_hour_low: 3000 gp_per_hour_high: 6000 xp_per_hour: 14000 supplies: # everything the bot must carry / restock - item: nature rune qty_per_cycle: 1 source: ge ge_limit: 12500 # buy limit / 4h window (verified) - item: coal qty_per_cycle: 1 source: ge ge_limit: 8000 profit_drivers: # what actually makes it profitable - "Low alch value vs rune+material buy price" # ---------------------------------------------------------------- state state_machine: initial: idle terminal: [complete, failed] states: idle: goal: reach the banking/crafting location next: [crafting, bank_supplies] crafting: goal: cast Low Level Alchemy on a sellable item next: [bank_supplies, idle, complete, failed] bank_supplies: goal: bank products, withdraw fresh supplies next: [crafting, idle] complete: goal: hit the run's completion condition failed: goal: a hard failure — stop, log, alert transitions: - from: crafting when: "runes or materials < buffer" to: bank_supplies - from: idle when: "target level or gp goal reached" to: complete # ---------------------------------------------------------------- actions actions: - id: a1 step: reach-bank verb: move_to # <- universal verb (action-grammar.md) target: bank params: location: "Bank of Gielinor" method: [teleport, walk] # preferred, fallback on_fail: retry # retry | skip | abort | alert anti_ban: # OPTIONAL humanisation hints mouse: "wander 20-80px around target" delay: [400, 1600] - id: a2 step: cast-alch verb: use_item_on target: "sellable item in inventory" params: item: "lowest-alch-value inventory item" on: "itself" on_fail: retry anti_ban: delay: [900, 2400] - id: a3 step: bank-loop verb: bank_inventory target: bank params: deposit: [all products] withdraw: "supplies up to buffer" on_fail: alert # ---------------------------------------------------------------- loop / pacing run_control: iteration: "one full craft+bank cycle" max_iterations: null # null = until complete condition break: # humanised pause policy every: [8, 14] # cycles for: [60, 180] # seconds stop_conditions: - "target level reached" - "gp under floor" - "hard error 3x" # ---------------------------------------------------------------- safety risk: ban_level: low # low | medium | high notes: - "Rune purchase bursts are the main detection vector; pace GE buys." # ---------------------------------------------------------------- provenance sources: - "OSRS Wiki — Low level alchemy (price/limit verified 2026-08-27)" - "prices.runescape.wiki mapping (item ids + GE limits)" verified: 2026-08-27 related: - mule-topup - f2p-alch-catalog ``` Every key except `actions[].anti_ban`, `notes`, `params` details is **required** (enforced by `schemas/entry.schema.json`). The validator is strict — a draft that fails validation blocks the PR. --- ## Best-in-class rule (required) Every entry MUST declare how a top operator actually runs the method, in `best_practice`. This is where "staff of fire for alchemy", "noted items in the spell slot", "marks of grace", "best prayer for the boss" etc. live. - `equipment[]` — item + `why` (mechanical reason) + `source`. - `techniques[]` — named technique + `why` + `source`. - Every line carries a `source`: an OSRS wiki page, **or** `researched ` for a fact not yet documented on the wiki — a `researched` flag is a TODO: a follow-up issue must land the proper citation. - The translator MUST adopt the best-practice setup by default. Deviating requires `improvised: true` in the translation header with a reason. ## Translation contract (agent -> client) When an agent picks up an entry to build a script: 1. Read `state_machine` → build the client's state machine 1:1 (same state names, same transition conditions). 2. Walk `actions[]` in order; map each `verb` using the client's mapping table in `docs/action-grammar.md` (DreamBot column, Microbot column, ...). 3. Apply `run_control` pacing and `break` policy verbatim. 4. Apply every `anti_ban` hint; the agent may **add** humanisation but must never remove a codex-mandated one. 5. Emit a `translation` block in the generated script header: codex `id`, `version`, client name, and any verb the agent had to improvise (flagged `improvised: true`). If a verb has no mapping for the target client, the agent MUST mark it `improvised: true` and leave a `TODO(client)` — never silently invent behaviour.