From d6eb514b3e93489b1f93641dbb7735c0dc16f632 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 27 Aug 2026 03:30:47 -0500 Subject: [PATCH 1/7] codex/training/mining-f2p.yaml: F2P mining entry (issue #4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wiki-verified 2026-08-27 (Free-to-play Mining training guide): - F2P ore chain: clay 1/5xp, copper 1/17.5, tin 1/17.5, iron 15/35, silver 20/40, coal 30/50, gold 40/65, mithril 55/80, adamantite 70/95, runite 85/125; pickaxe tiers bronze->rune with roll intervals (rune = 3 ticks). - Rates: iron powermine ~50,000 xp/hr at 41+ with rune pickaxe, 52,500 at 61+; Al Kharid = only F2P 3-rock spot; Doric's Quest 1,300 xp. - Money: adamantite (70) banking; runite (85) cited as fastest F2P money. Prices snapshot 2026-08-27 (prices.runescape.wiki latest): iron 70, coal 151, silver 73, gold 154, mithril 159, adamantite 584, runite 10265 (id 451). Gate: RESULT 2 entries, 0 errors, 1 warning (pre-existing researched flag on low-alch-f2p.yaml on main — resolved by PR !10, not this branch). --- codex/training/mining-f2p.yaml | 208 +++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 codex/training/mining-f2p.yaml diff --git a/codex/training/mining-f2p.yaml b/codex/training/mining-f2p.yaml new file mode 100644 index 0000000..98bfea7 --- /dev/null +++ b/codex/training/mining-f2p.yaml @@ -0,0 +1,208 @@ +id: mining-f2p +title: Mining (F2P) +domain: training +tags: [mining, gathering, f2p, skill-training] +status: verified +version: 1 +updated: "2026-08-27" # ISO dates must stay quoted — YAML 1.1 parses bare dates to datetime.date + +summary: > + F2P mining: mine the highest available ore tier with the best pickaxe, + banking the ore (money route) or dropping it (powermine XP route). The + wiki's free-to-play training guide is the authoritative progression: + clay/copper/tin from level 1, iron 15 (the fast-XP ore), silver 20, coal + 30, gold 40, mithril 55, adamantite 70, runite 85. Mining is a staple + low-risk, low-input method with no respawn mechanics in the loop. + +accounts: + membership: f2p # whole ore chain verified F2P via the wiki guide + min_level: 1 # clay/copper/tin mineable at level 1 + requires_quest: [] + requires_other: + - "Best-tier F2P pickaxe available (bronze -> rune; equip it to save an inventory slot)" + - "Bank route from the chosen mine (or an inventory-drop spot for powermining)" + - "Al Kharid powermining: survive aggressive level-14 scorpions (food/prayer at low levels)" + +best_practice: + equipment: + - item: highest-tier pickaxe available (bronze -> rune) + why: "each tier is faster — the roll interval drops from 8 ticks (bronze) to 3 ticks (rune), and the guide recommends always using the highest tier available; equipping it frees an inventory slot" + source: "OSRS wiki — Free-to-play Mining training (Pickaxes: tier table with tick intervals)" + techniques: + - name: start with Doric's Quest for 1,300 xp (level 1 to 10) + why: "the quest reward alone takes Mining from 1 to 10; the materials (6 clay, 4 copper, 2 iron) mine at level 1 except iron, which can be bought on the GE" + source: "OSRS wiki — Free-to-play Mining training (Level 1-10: Doric's Quest)" + - name: powermine iron at Al Kharid mine for maximum XP + why: "iron (level 15, 35 xp) is the fastest F2P training ore and Al Kharid is the only F2P site with three iron rocks around one tile — zero travel between respawns; ~50,000 xp/hr at 41 Mining with a rune pickaxe, 52,500 at 61+ (3 ores per 12 ticks); drop the ores — banking costs XP" + source: "OSRS wiki — Free-to-play Mining training (Levels 15-99: Iron ore)" + - name: bank iron at the South-east Varrock mine for smithing/money + why: "the guide names this F2P banking location explicitly; the eastern Varrock bank is the closest bank leg, and the ores feed Smithing" + source: "OSRS wiki — Free-to-play Mining training (Levels 1/10-15 and 15-99: banking locations)" + - name: "coal route: West Lumbridge Swamp mine, or Superheat in place" + why: "coal (level 30, 50 xp) banks reasonably from the Lumbridge Swamp mine; with 43 Magic, Superheat Item converts coal into bars without a furnace, cutting bank trips (staff of fire + nature runes, 26 inventory slots)" + source: "OSRS wiki — Free-to-play Mining training (Levels 30-99: Superheat Item)" + - name: bank-distance tradeoff + why: "farther mines become viable when bank travel is proportionally small (superheat method) — pick the mine whose bank leg costs the least per trip for the mode being run" + source: "OSRS wiki — Free-to-play Mining training (Levels 30-99: Superheat Item — mines farther from a bank become viable)" + +economy: + # Snapshot 2026-08-27, prices.runescape.wiki latest (sell = low edge). + # Cycle = one rock mined -> one ore (XP path) or one ore banked (money path). + # XP rates sourced from the wiki guide (iron powermine); gp/h model below is + # derived and labelled (no wiki gp/hr figure exists for banked iron). + item: Iron ore # anchor: the F2P training ore + item_id: 440 + cost_per_cycle: 0 # mining consumes nothing (pickaxe has no fuel; no supply costs) + income_per_cycle: 70 # iron ore sell price (low edge), snapshot 2026-08-27 + net_per_cycle: 70 # no per-cycle costs; selling optional (powermine drops -> 0) + gp_per_hour_low: 0 # powermine mode drops ores — XP is the product + gp_per_hour_high: 28000 # MODELLED (dated): ~400 banked iron ores/hr (bank legs reduce the 52.5k-xp/hr powermine rate) x 70 gp + xp_per_hour: 52500 # iron powermine, 61+ (50,000 at 41+ with rune pickaxe) — wiki guide + supplies: + - item: rune pickaxe # highest F2P tier; withdrawn from bank at session start + qty_per_cycle: 1 + source: bank + profit_drivers: + - "F2P ore chain (wiki guide): clay 1/5xp, copper 1/17.5, tin 1/17.5, iron 15/35, silver 20/40, coal 30/50, gold 40/65, mithril 55/80, adamantite 70/95, runite 85/125" + - "Sell prices 2026-08-27 (low edge): iron 70, coal 151, silver 73, gold 154, mithril 159, adamantite 584, runite 10,265" + - "Money routes: adamantite banking is a good F2P money method (70 Mining); runite (85 Mining) is cited as one of the fastest F2P money-making methods" + - "For XP, powermining iron beats every banked route — drop the ores (guide-recommended)" + +state_machine: + initial: idle + terminal: [complete, failed] + states: + idle: + goal: be at the chosen mine with pickaxe equipped and inventory clear + next: [mine, complete, failed] + mine: + goal: mine the current tier's rock until the inventory fills or the rotation ends + next: [bank_run, drop_run, complete, failed] + bank_run: + goal: walk to the bank, deposit ores, walk back to the mine + next: [mine, idle] + drop_run: + goal: drop the ore stack at a safe spot and resume mining + next: [mine] + complete: + goal: level or GP target reached + failed: + goal: hard failure — stop, log ERROR sentinel, alert + transitions: + - from: mine + when: "inventory full (banked mode)" + to: bank_run + - from: mine + when: "inventory full (powermine mode)" + to: drop_run + - from: bank_run + when: "ores deposited and back at the mine" + to: mine + - from: drop_run + when: "stack dropped" + to: mine + - from: idle + when: "level or GP target reached" + to: complete + - from: mine + when: "level or GP target reached" + to: complete + +actions: + - id: a1 + step: reach-mine + verb: move_to + target: "selected F2P mine" + params: + location: "Al Kharid Mine (powermine) or South-east Varrock mine (banked)" + method: [walk, teleport] + note: "arrive with the best-tier pickaxe equipped" + on_fail: retry + anti_ban: + delay: [400, 1600] + + - id: a2 + step: mine-rock + verb: click_object + target: "current tier rock" + params: + rock: "iron rock (tier-gated: silver 20, coal 30, gold 40, mithril 55, adamantite 70, runite 85)" + gate: "level_is Mining >= rock level" + rotation: "3-rock cluster; face the respawning rock (rune pickaxe, 3 ticks)" + on_fail: retry + anti_ban: + delay: [600, 1400] # sub-1.8s click cadence is the bot tell; keep jitter above it + + - id: a3 + step: bank-ores + verb: move_to + target: bank + params: + location: "nearest bank to the mine (east Varrock for SE Varrock mine, Lumbridge Castle for the Swamp mine)" + method: [walk, teleport] + on_fail: retry + anti_ban: + delay: [400, 1200] + + - id: a4 + step: deposit + verb: bank_inventory + target: bank + params: + deposit: [all ores] + withdraw: "nothing (banked mode)" + on_fail: alert + anti_ban: + delay: [500, 1100] + + - id: a5 + step: return-mine + verb: move_to + target: "the mine" + params: + method: [walk, teleport] + on_fail: retry + anti_ban: + delay: [400, 1200] + + - id: a6 + step: drop-ores + verb: right_click + target: "ore stack in inventory" + params: + option: "Drop" + mode: "powermine only" + on_fail: skip + anti_ban: + delay: [200, 800] # drop churn on a full inventory is human-normal + +run_control: + iteration: "one full mine -> bank (or drop) rotation" + max_iterations: null + break: + every: [8, 14] # rotations + for: [60, 180] # seconds of idle AFK + stop_conditions: + - "level or GP target reached" + - "3 consecutive failed rock clicks (client issue — do not loop)" + # Respawn: N/A in this loop — no death/respawn mechanics (no combat in F2P + # mining; Al Kharid's aggressive scorpions are handled via food/prayer, not death). + +risk: + ban_level: low + notes: + - "Mining is a staple low-risk method: long, repetitive, low-input behaviour that matches normal player sessions — the guide itself recommends hours of drop-mining." + - "The 3-tick powermine rotation is the detection vector if executed perfectly for hours — jitter the rotation cadence and take the break schedule." + - "Al Kharid mine has aggressive level-14 scorpions; at low levels carry food or prayer points (guide notes this)." + +sources: + - "OSRS wiki — Free-to-play Mining training (ore level/xp table — all rows F2P; pickaxe tier table with tick intervals; Doric's Quest 1,300 xp; iron 50,000 xp/hr at 41+ / 52,500 at 61+; Al Kharid 3-rock spot; SE Varrock banking; Superheat coal; adamantite/runite as F2P money methods) — verified 2026-08-27" + - "OSRS wiki — Mining (skill overview, members=No)" + - "prices.runescape.wiki/api/v1/osrs/mapping (ore ids: iron 440, coal 453, silver 442, gold 444, mithril 447, adamantite 449, runite 451 — all members=false; buy limits)" + - "prices.runescape.wiki/api/v1/osrs/latest (ore sell prices, snapshot 2026-08-27)" +verified: "2026-08-27" # keep quoted (YAML 1.1 date parsing) + +related: + - magic-alch-handoff + - f2p-alch-catalog + - mule-topup From b06b168c5558d43164fa74406e0df157c0ab0541 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 27 Aug 2026 12:49:58 -0500 Subject: [PATCH 2/7] codex: fix mining-f2p entry per governor review (bounce fix attempt 1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Powermine drop loop: model per-slot Dropping of non-stackable ores — drop_run now self-loops (count_item ore > 0 -> drop_run) until the inventory is empty, then back to mine; a6 drops one occupied slot per execution and its params state the iteration contract. - Click cadence: a2 delay was 600-1400ms, below the rune pickaxe 3-tick (1.8s) mining roll — replaced with 2000-4200ms and comment distinguishing the roll interval from translator re-click cadence (bronze 8 ticks = 4.8s note). - Al Kharid survivability: route gate modeled - scorpion is level 14 and attacks players at combat <= 28 (wiki Aggressiveness, 2x rule), so Al Kharid powermine requires combat 29+ or food; new survive state with eat/ retreat actions (a7/a8), hard-failure path (food exhausted + low HP -> failed; death = hard failure, no respawn claim). Banked South-east Varrock route preserved for sub-29 accounts. version 1 -> 2. Validator: 0 errors, 1 pre-existing F2P WARN. --- codex/training/mining-f2p.yaml | 78 ++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 17 deletions(-) diff --git a/codex/training/mining-f2p.yaml b/codex/training/mining-f2p.yaml index 98bfea7..8185e61 100644 --- a/codex/training/mining-f2p.yaml +++ b/codex/training/mining-f2p.yaml @@ -3,7 +3,7 @@ title: Mining (F2P) domain: training tags: [mining, gathering, f2p, skill-training] status: verified -version: 1 +version: 2 # 2: bounce-fix 2026-08-27 — per-slot drop loop, click-cadence model, Al Kharid survivability gate updated: "2026-08-27" # ISO dates must stay quoted — YAML 1.1 parses bare dates to datetime.date summary: > @@ -12,7 +12,10 @@ summary: > wiki's free-to-play training guide is the authoritative progression: clay/copper/tin from level 1, iron 15 (the fast-XP ore), silver 20, coal 30, gold 40, mithril 55, adamantite 70, runite 85. Mining is a staple - low-risk, low-input method with no respawn mechanics in the loop. + low-risk, low-input method with no respawn mechanics in the loop. The Al + Kharid powermine is gated: its level-14 scorpions attack players at combat + level 28 or below (wiki Aggressiveness), so sub-29 accounts carry food or + use the banked South-east Varrock route instead. accounts: membership: f2p # whole ore chain verified F2P via the wiki guide @@ -21,7 +24,7 @@ accounts: requires_other: - "Best-tier F2P pickaxe available (bronze -> rune; equip it to save an inventory slot)" - "Bank route from the chosen mine (or an inventory-drop spot for powermining)" - - "Al Kharid powermining: survive aggressive level-14 scorpions (food/prayer at low levels)" + - "Al Kharid powermine route gate: combat level >= 29 (a monster attacks players at <= 2x its level; the scorpion is level 14, so 29+ is safe) OR carry food; below the gate use the banked South-east Varrock route (no aggressive NPCs)" best_practice: equipment: @@ -82,8 +85,11 @@ state_machine: goal: walk to the bank, deposit ores, walk back to the mine next: [mine, idle] drop_run: - goal: drop the ore stack at a safe spot and resume mining - next: [mine] + goal: "drop EVERY ore slot: one right-click Drop per slot (a6), looping until the inventory is empty (non-stackable ores: 27 slots, 27 Drops)" + next: [drop_run, mine] + survive: + goal: "Al Kharid scorpion aggro: eat when HP < 40%; retreat out of the aggro radius if overwhelmed; return to the 3-rock cluster when safe" + next: [mine, failed] complete: goal: level or GP target reached failed: @@ -93,14 +99,26 @@ state_machine: when: "inventory full (banked mode)" to: bank_run - from: mine - when: "inventory full (powermine mode)" + when: "inventory full (powermine mode: 27 ore slots)" to: drop_run + - from: drop_run + when: "inventory still holds ore (count_item ore > 0)" + to: drop_run + - from: drop_run + when: "inventory empty (count_item ore == 0)" + to: mine + - from: mine + when: "HP < 40% (scorpion aggro at Al Kharid, sub-combat-29 run)" + to: survive + - from: survive + when: "HP >= 60% and out of combat" + to: mine + - from: survive + when: "food exhausted and HP < 40%" + to: failed - from: bank_run when: "ores deposited and back at the mine" to: mine - - from: drop_run - when: "stack dropped" - to: mine - from: idle when: "level or GP target reached" to: complete @@ -117,6 +135,7 @@ actions: location: "Al Kharid Mine (powermine) or South-east Varrock mine (banked)" method: [walk, teleport] note: "arrive with the best-tier pickaxe equipped" + route_gate: "Al Kharid powermine only at combat >= 29 (scorpions attack <= 28) or with food; otherwise South-east Varrock (banked, no aggressive NPCs)" on_fail: retry anti_ban: delay: [400, 1600] @@ -128,10 +147,10 @@ actions: params: rock: "iron rock (tier-gated: silver 20, coal 30, gold 40, mithril 55, adamantite 70, runite 85)" gate: "level_is Mining >= rock level" - rotation: "3-rock cluster; face the respawning rock (rune pickaxe, 3 ticks)" + rotation: "3-rock cluster; face the respawning rock (rune pickaxe, 3 ticks = 1.8s per roll)" on_fail: retry anti_ban: - delay: [600, 1400] # sub-1.8s click cadence is the bot tell; keep jitter above it + delay: [2000, 4200] # re-click cadence vs mining roll: rune pickaxe rolls every 3 ticks (1.8s) — clicking faster than the roll cannot speed the ore and IS the bot tell; 2.0-4.2s ≈ one click per roll/break cycle, humanised. Lower-tier pickaxes roll slower (bronze = 8 ticks = 4.8s): scale the pause above the roll interval. - id: a3 step: bank-ores @@ -168,14 +187,38 @@ actions: - id: a6 step: drop-ores verb: right_click - target: "ore stack in inventory" + target: "ore slot in inventory" params: option: "Drop" + iterate: "one occupied ore slot per execution — non-stackable ores need one Drop per slot; the drop_run -> drop_run transition re-executes a6 until count_item ore == 0" mode: "powermine only" on_fail: skip anti_ban: delay: [200, 800] # drop churn on a full inventory is human-normal + - id: a7 + step: eat-food + verb: eat + target: "food in inventory" + params: + threshold: "HP < 40%" + mode: "Al Kharid powermine only (scorpion aggro, sub-combat-29 run)" + supply: "session-level, not per-cycle: withdraw ~20 trout/salmon at the bank before a sub-combat-29 Al Kharid run" + on_fail: alert # no food left at low HP — the survive -> failed transition is the hard failure + anti_ban: + delay: [400, 1200] + + - id: a8 + step: retreat-scorpions + verb: retreat + target: "self" + params: + from: "scorpion aggro radius (Al Kharid mine)" + method: "walk out of attack range, then re-enter the 3-rock cluster once out of combat" + on_fail: retry + anti_ban: + delay: [600, 1800] + run_control: iteration: "one full mine -> bank (or drop) rotation" max_iterations: null @@ -185,18 +228,19 @@ run_control: stop_conditions: - "level or GP target reached" - "3 consecutive failed rock clicks (client issue — do not loop)" - # Respawn: N/A in this loop — no death/respawn mechanics (no combat in F2P - # mining; Al Kharid's aggressive scorpions are handled via food/prayer, not death). + - "death: hard failure — the Al Kharid gate (combat 29+) plus the survive state prevent it; if is_dead anyway (gate bypassed/griefed) log ERROR + alert and stop, no respawn loop" risk: ban_level: low notes: - "Mining is a staple low-risk method: long, repetitive, low-input behaviour that matches normal player sessions — the guide itself recommends hours of drop-mining." - - "The 3-tick powermine rotation is the detection vector if executed perfectly for hours — jitter the rotation cadence and take the break schedule." - - "Al Kharid mine has aggressive level-14 scorpions; at low levels carry food or prayer points (guide notes this)." + - "The 3-tick powermine rotation is the detection vector if executed perfectly for hours — jitter the rotation cadence (a2 delay 2.0-4.2s, above the 1.8s rune roll) and take the break schedule." + - "Al Kharid mine has aggressive level-14 scorpions: they attack players at combat level <= 28 (wiki Aggressiveness). Gate: run Al Kharid at combat 29+, or below that carry food (survive state eats/retreats; food out + low HP = hard failure). The banked South-east Varrock route has no aggressive NPCs." sources: - - "OSRS wiki — Free-to-play Mining training (ore level/xp table — all rows F2P; pickaxe tier table with tick intervals; Doric's Quest 1,300 xp; iron 50,000 xp/hr at 41+ / 52,500 at 61+; Al Kharid 3-rock spot; SE Varrock banking; Superheat coal; adamantite/runite as F2P money methods) — verified 2026-08-27" + - "OSRS wiki — Active/passive aggressive monsters: Aggressiveness (a monster attacks players whose combat level is <= 2x its level; a level-14 scorpion is safe from combat level 29) — verified 2026-08-27" + - "OSRS wiki — Scorpion (combat level 14, hitpoints 17; Al Kharid mine) — verified 2026-08-27" + - "OSRS wiki — Free-to-play Mining training (ore level/xp table — all rows F2P; pickaxe tier table with tick intervals; Doric's Quest 1,300 xp; iron 50,000 xp/hr at 41+ / 52,500 at 61+; Al Kharid 3-rock spot; SE Varrock banking; Superheat coal; adamantite/runite as F2P money methods; scorpions: 'bring food') — verified 2026-08-27" - "OSRS wiki — Mining (skill overview, members=No)" - "prices.runescape.wiki/api/v1/osrs/mapping (ore ids: iron 440, coal 453, silver 442, gold 444, mithril 447, adamantite 449, runite 451 — all members=false; buy limits)" - "prices.runescape.wiki/api/v1/osrs/latest (ore sell prices, snapshot 2026-08-27)" From ba3a8af9f97d13d325be3703166798881b6ee68e Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 27 Aug 2026 17:00:42 -0500 Subject: [PATCH 3/7] codex: fix mining-f2p entry per governor review (bounce fix attempt 2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Dynamic inventory/drop model: no hardcoded 27-slot count; drop loop driven by count_item ore + inventory full; equip only when Attack requirement met (rune = 40 Attack), else carry pickaxe in inventory - Outcome wait: new wait_rock state — re-click only on ore gained, rock depleted, or roll timeout; never re-click the active rock - Tier-aware cadence: roll_table per pickaxe tier (bronze 4.8s -> rune 1.8s), mandatory pause >= full roll + [0,1800]ms; anchor rune [1800,3600] - Al Kharid hard-gated to combat 29+ (scorpion aggro ceiling 28 per wiki Aggressiveness); sub-29 -> SE Varrock, no food in the model, eat action removed; survive = retreat-only defensive fallback - version 2 -> 3 --- codex/training/mining-f2p.yaml | 118 ++++++++++++++++++--------------- 1 file changed, 66 insertions(+), 52 deletions(-) diff --git a/codex/training/mining-f2p.yaml b/codex/training/mining-f2p.yaml index 8185e61..22240e3 100644 --- a/codex/training/mining-f2p.yaml +++ b/codex/training/mining-f2p.yaml @@ -3,7 +3,7 @@ title: Mining (F2P) domain: training tags: [mining, gathering, f2p, skill-training] status: verified -version: 2 # 2: bounce-fix 2026-08-27 — per-slot drop loop, click-cadence model, Al Kharid survivability gate +version: 3 # 3: bounce-fix 2 2026-08-27 — dynamic inventory/drop model (no hardcoded slot count), conditional equip, wait_rock outcome gate, tier-aware cadence table, Al Kharid hard-gated to combat 29+ (no food in model) updated: "2026-08-27" # ISO dates must stay quoted — YAML 1.1 parses bare dates to datetime.date summary: > @@ -13,33 +13,34 @@ summary: > clay/copper/tin from level 1, iron 15 (the fast-XP ore), silver 20, coal 30, gold 40, mithril 55, adamantite 70, runite 85. Mining is a staple low-risk, low-input method with no respawn mechanics in the loop. The Al - Kharid powermine is gated: its level-14 scorpions attack players at combat - level 28 or below (wiki Aggressiveness), so sub-29 accounts carry food or - use the banked South-east Varrock route instead. + Kharid powermine is hard-gated: its level-14 scorpions attack players at + combat level 28 or below (wiki Aggressiveness), so the mine runs only at + combat 29+; sub-29 accounts use the banked South-east Varrock route, + which has no aggressive NPCs and needs no food. accounts: membership: f2p # whole ore chain verified F2P via the wiki guide min_level: 1 # clay/copper/tin mineable at level 1 requires_quest: [] requires_other: - - "Best-tier F2P pickaxe available (bronze -> rune; equip it to save an inventory slot)" + - "Best-tier F2P pickaxe the account can wield (bronze: no requirement -> rune: 40 Attack). Equip it ONLY when the Attack requirement is met; otherwise carry it in the inventory — mining from the inventory works at any level (equipped pickaxe = 28 ore slots, carried = 27; slot counts derive dynamically, never hardcoded)" - "Bank route from the chosen mine (or an inventory-drop spot for powermining)" - - "Al Kharid powermine route gate: combat level >= 29 (a monster attacks players at <= 2x its level; the scorpion is level 14, so 29+ is safe) OR carry food; below the gate use the banked South-east Varrock route (no aggressive NPCs)" + - "Al Kharid powermine HARD GATE: combat level >= 29 (a monster attacks players at <= 2x its level; the scorpion is level 14, so its aggro ceiling is combat 28 and 29+ is safe). Sub-combat-29 accounts MUST use the banked South-east Varrock route (no aggressive NPCs, no food required)" best_practice: equipment: - - item: highest-tier pickaxe available (bronze -> rune) - why: "each tier is faster — the roll interval drops from 8 ticks (bronze) to 3 ticks (rune), and the guide recommends always using the highest tier available; equipping it frees an inventory slot" + - item: highest-tier pickaxe the account can wield (bronze -> rune) + why: "each tier is faster — the roll interval drops from 8 ticks (bronze) to 3 ticks (rune), and the guide recommends always using the highest tier available; equipping it frees an inventory slot. Equip only when the Attack requirement is met (rune = 40 Attack); below that, mine with the pickaxe in the inventory (legal at every level)" source: "OSRS wiki — Free-to-play Mining training (Pickaxes: tier table with tick intervals)" techniques: - name: start with Doric's Quest for 1,300 xp (level 1 to 10) why: "the quest reward alone takes Mining from 1 to 10; the materials (6 clay, 4 copper, 2 iron) mine at level 1 except iron, which can be bought on the GE" source: "OSRS wiki — Free-to-play Mining training (Level 1-10: Doric's Quest)" - - name: powermine iron at Al Kharid mine for maximum XP - why: "iron (level 15, 35 xp) is the fastest F2P training ore and Al Kharid is the only F2P site with three iron rocks around one tile — zero travel between respawns; ~50,000 xp/hr at 41 Mining with a rune pickaxe, 52,500 at 61+ (3 ores per 12 ticks); drop the ores — banking costs XP" - source: "OSRS wiki — Free-to-play Mining training (Levels 15-99: Iron ore)" - - name: bank iron at the South-east Varrock mine for smithing/money - why: "the guide names this F2P banking location explicitly; the eastern Varrock bank is the closest bank leg, and the ores feed Smithing" + - name: powermine iron at Al Kharid mine for maximum XP (combat 29+ only) + why: "iron (level 15, 35 xp) is the fastest F2P training ore and Al Kharid is the only F2P site with three iron rocks around one tile — zero travel between respawns; ~50,000 xp/hr at 41 Mining with a rune pickaxe, 52,500 at 61+ (3 ores per 12 ticks); drop the ores — banking costs XP. Entry gate: the mine's level-14 scorpions are aggressive to combat 28, so this spot runs at combat 29+" + source: "OSRS wiki — Free-to-play Mining training (Levels 15-99: Iron ore); OSRS wiki — Aggressiveness" + - name: bank iron at the South-east Varrock mine for smithing/money (sub-29 default) + why: "the guide names this F2P banking location explicitly; the eastern Varrock bank is the closest bank leg, and the ores feed Smithing. This is also the sub-combat-29 route (no aggressive NPCs)" source: "OSRS wiki — Free-to-play Mining training (Levels 1/10-15 and 15-99: banking locations)" - name: "coal route: West Lumbridge Swamp mine, or Superheat in place" why: "coal (level 30, 50 xp) banks reasonably from the Lumbridge Swamp mine; with 43 Magic, Superheat Item converts coal into bars without a furnace, cutting bank trips (staff of fire + nature runes, 26 inventory slots)" @@ -53,6 +54,10 @@ economy: # Cycle = one rock mined -> one ore (XP path) or one ore banked (money path). # XP rates sourced from the wiki guide (iron powermine); gp/h model below is # derived and labelled (no wiki gp/hr figure exists for banked iron). + # Cadence consistency: the 52,500 xp/hr figure IS the continuous 61+ rotation + # (rune pickaxe): 52,500 / 35 xp = 1,500 ores/hr = one ore per 2.4s, which is + # the rune roll (1.8s) + wait_rock outcome gate + anti_ban delay [1800, 3600] + # midpoint (2.7s) — the modeled a2 cadence brackets the wiki rate. item: Iron ore # anchor: the F2P training ore item_id: 440 cost_per_cycle: 0 # mining consumes nothing (pickaxe has no fuel; no supply costs) @@ -62,7 +67,7 @@ economy: gp_per_hour_high: 28000 # MODELLED (dated): ~400 banked iron ores/hr (bank legs reduce the 52.5k-xp/hr powermine rate) x 70 gp xp_per_hour: 52500 # iron powermine, 61+ (50,000 at 41+ with rune pickaxe) — wiki guide supplies: - - item: rune pickaxe # highest F2P tier; withdrawn from bank at session start + - item: "pickaxe: highest tier the account can wield (rune needs 40 Attack to equip; below that carried in the inventory)" qty_per_cycle: 1 source: bank profit_drivers: @@ -76,19 +81,22 @@ state_machine: terminal: [complete, failed] states: idle: - goal: be at the chosen mine with pickaxe equipped and inventory clear + goal: "be at the chosen mine with the pickaxe ready (equipped only if the account meets its Attack requirement) and the inventory clear; run mode chosen at session start (powermine: drop ores; banked: bank ores)" next: [mine, complete, failed] mine: - goal: mine the current tier's rock until the inventory fills or the rotation ends - next: [bank_run, drop_run, complete, failed] + goal: "mine the current tier's rock until the inventory is full or the target is reached; every click (a2) is followed by the wait_rock outcome wait — never chain clicks on the same rock" + next: [wait_rock, drop_run, bank_run, complete, failed] + wait_rock: + goal: "after clicking a rock (a2), wait for the mining outcome — ore gained, rock depleted, or roll timeout — before any re-click; never re-click the active rock while its roll is unresolved" + next: [mine, drop_run, bank_run, failed] bank_run: - goal: walk to the bank, deposit ores, walk back to the mine + goal: "walk to the bank, deposit ores, walk back to the mine" next: [mine, idle] drop_run: - goal: "drop EVERY ore slot: one right-click Drop per slot (a6), looping until the inventory is empty (non-stackable ores: 27 slots, 27 Drops)" + goal: "drop every occupied ore slot: one right-click Drop per slot (a6), looping until the inventory holds no ore — slot count is DYNAMIC (inventory capacity minus non-ore items: equipped pickaxe = 28 ore slots, carried = 27, survival/gear load = fewer); drive the loop off count_item ore, never hardcode a slot number" next: [drop_run, mine] survive: - goal: "Al Kharid scorpion aggro: eat when HP < 40%; retreat out of the aggro radius if overwhelmed; return to the 3-rock cluster when safe" + goal: "defensive fallback only (the 29+ gate means scorpions never aggro at Al Kharid): if attacked anyway (gate bypassed/griefed), retreat out of the aggro radius and re-enter the cluster when safe; no food loop is modeled" next: [mine, failed] complete: goal: level or GP target reached @@ -96,25 +104,40 @@ state_machine: goal: hard failure — stop, log ERROR sentinel, alert transitions: - from: mine + when: "a2 rock click issued (the tier roll is now in progress)" + to: wait_rock + - from: wait_rock + when: "ore gained (count_item ore > count at click) or the clicked rock depleted" + to: mine + - from: wait_rock + when: "roll timeout (no outcome within 2x the equipped tier's roll interval)" + to: mine + - from: wait_rock + when: "inventory full (powermine mode)" + to: drop_run + - from: wait_rock when: "inventory full (banked mode)" to: bank_run - from: mine - when: "inventory full (powermine mode: 27 ore slots)" + when: "inventory full (powermine mode)" to: drop_run + - from: mine + when: "inventory full (banked mode)" + to: bank_run - from: drop_run when: "inventory still holds ore (count_item ore > 0)" to: drop_run - from: drop_run - when: "inventory empty (count_item ore == 0)" + when: "inventory empty of ore (count_item ore == 0)" to: mine - from: mine - when: "HP < 40% (scorpion aggro at Al Kharid, sub-combat-29 run)" + when: "HP < 40% (hp_is — defensive only; unexpected aggro despite the gate)" to: survive - from: survive - when: "HP >= 60% and out of combat" + when: "out of combat (HP stable, no attacker in range)" to: mine - from: survive - when: "food exhausted and HP < 40%" + when: "is_dead (gate bypassed/griefed)" to: failed - from: bank_run when: "ores deposited and back at the mine" @@ -134,8 +157,8 @@ actions: params: location: "Al Kharid Mine (powermine) or South-east Varrock mine (banked)" method: [walk, teleport] - note: "arrive with the best-tier pickaxe equipped" - route_gate: "Al Kharid powermine only at combat >= 29 (scorpions attack <= 28) or with food; otherwise South-east Varrock (banked, no aggressive NPCs)" + note: "arrive with the pickaxe equipped ONLY if the account meets its Attack requirement (rune = 40 Attack); otherwise carry it in the inventory" + route_gate: "Al Kharid powermine ONLY at combat level >= 29 (level_is Combat >= 29 — the level-14 scorpion's aggro ceiling is combat 28, wiki Aggressiveness); below combat 29 use the banked South-east Varrock route (no aggressive NPCs, no food required)" on_fail: retry anti_ban: delay: [400, 1600] @@ -143,14 +166,17 @@ actions: - id: a2 step: mine-rock verb: click_object - target: "current tier rock" + target: "fresh rock in the cluster — never the rock whose roll is in progress" params: rock: "iron rock (tier-gated: silver 20, coal 30, gold 40, mithril 55, adamantite 70, runite 85)" gate: "level_is Mining >= rock level" - rotation: "3-rock cluster; face the respawning rock (rune pickaxe, 3 ticks = 1.8s per roll)" + rotation: "3-rock cluster (Al Kharid) or nearest cluster of the selected tier; face the respawning rock and click a fresh rock only after the wait_rock outcome wait" + roll_table: "pickaxe roll interval (ticks x 0.6s, wiki table verified 2026-08-27): bronze 8 = 4.8s, iron 7 = 4.2s, steel 6 = 3.6s, mithril 5 = 3.0s, adamant 4 = 2.4s, rune 3 = 1.8s" + cadence: "mandatory pause per click = equipped pickaxe roll interval from roll_table + [0, 1800]ms humanisation (anchor model below: rune pickaxe @ 61+ -> delay [1800, 3600]; bronze users apply [4800, 6600], iron [4200, 6000], steel [3600, 5400], mithril [3000, 4800], adamant [2400, 4200]). The pause must never be shorter than the equipped tier's FULL roll, and the wait_rock outcome wait must elapse before any re-click — clicking before the roll resolves (or before the rock depletes) cannot speed the ore and is the bot tell" + wait_for: "mining outcome — modeled as the wait_rock state: re-click a fresh rock only when ore was gained, the clicked rock depleted, or the roll timed out (2x roll)" on_fail: retry anti_ban: - delay: [2000, 4200] # re-click cadence vs mining roll: rune pickaxe rolls every 3 ticks (1.8s) — clicking faster than the roll cannot speed the ore and IS the bot tell; 2.0-4.2s ≈ one click per roll/break cycle, humanised. Lower-tier pickaxes roll slower (bronze = 8 ticks = 4.8s): scale the pause above the roll interval. + delay: [1800, 3600] # rune-pickaxe anchor (61+): full 1.8s roll + up to 1.8s humanisation; per-tier floor in params.roll_table — never below the equipped tier's full roll - id: a3 step: bank-ores @@ -190,57 +216,45 @@ actions: target: "ore slot in inventory" params: option: "Drop" - iterate: "one occupied ore slot per execution — non-stackable ores need one Drop per slot; the drop_run -> drop_run transition re-executes a6 until count_item ore == 0" + iterate: "one occupied ore slot per execution — non-stackable ores need one Drop per slot; the drop_run -> drop_run transition re-executes a6 until count_item ore == 0. Slot count is DYNAMIC (inventory capacity minus non-ore items: equipped pickaxe = 28, carried = 27) — never hardcode a slot number" mode: "powermine only" on_fail: skip anti_ban: delay: [200, 800] # drop churn on a full inventory is human-normal - id: a7 - step: eat-food - verb: eat - target: "food in inventory" - params: - threshold: "HP < 40%" - mode: "Al Kharid powermine only (scorpion aggro, sub-combat-29 run)" - supply: "session-level, not per-cycle: withdraw ~20 trout/salmon at the bank before a sub-combat-29 Al Kharid run" - on_fail: alert # no food left at low HP — the survive -> failed transition is the hard failure - anti_ban: - delay: [400, 1200] - - - id: a8 - step: retreat-scorpions + step: retreat-aggro verb: retreat target: "self" params: - from: "scorpion aggro radius (Al Kharid mine)" - method: "walk out of attack range, then re-enter the 3-rock cluster once out of combat" + from: "aggro radius at the mine (defensive fallback — the Al Kharid 29+ gate normally prevents all aggro)" + method: "walk out of attack range, then re-enter the cluster once out of combat" on_fail: retry anti_ban: delay: [600, 1800] run_control: - iteration: "one full mine -> bank (or drop) rotation" + iteration: "one full mine -> bank (or drop) rotation" # per docs/schema.md: break.every counts iterations max_iterations: null break: - every: [8, 14] # rotations + every: [8, 14] # iterations = full mine->bank/drop rotations for: [60, 180] # seconds of idle AFK stop_conditions: - "level or GP target reached" - "3 consecutive failed rock clicks (client issue — do not loop)" - - "death: hard failure — the Al Kharid gate (combat 29+) plus the survive state prevent it; if is_dead anyway (gate bypassed/griefed) log ERROR + alert and stop, no respawn loop" + - "death: hard failure — the Al Kharid hard gate (combat 29+) and the survive (retreat) fallback prevent it in normal operation; if is_dead anyway (gate bypassed/griefed) log ERROR + alert and stop, no respawn loop" risk: ban_level: low notes: - "Mining is a staple low-risk method: long, repetitive, low-input behaviour that matches normal player sessions — the guide itself recommends hours of drop-mining." - - "The 3-tick powermine rotation is the detection vector if executed perfectly for hours — jitter the rotation cadence (a2 delay 2.0-4.2s, above the 1.8s rune roll) and take the break schedule." - - "Al Kharid mine has aggressive level-14 scorpions: they attack players at combat level <= 28 (wiki Aggressiveness). Gate: run Al Kharid at combat 29+, or below that carry food (survive state eats/retreats; food out + low HP = hard failure). The banked South-east Varrock route has no aggressive NPCs." + - "The powermine rotation is the detection vector if executed perfectly for hours — keep the a2 tier-aware cadence (wait_rock outcome wait + roll_table pause; anchor rune 1.8-3.6s, never below the equipped tier's full roll) and take the break schedule." + - "Al Kharid mine has aggressive level-14 scorpions (they attack players at combat level <= 28 — wiki Aggressiveness). The entry HARD-gates Al Kharid to combat 29+; sub-29 accounts run the banked South-east Varrock route (no aggressive NPCs). No food is part of either route; the survive state (retreat) is a defensive fallback only." sources: - "OSRS wiki — Active/passive aggressive monsters: Aggressiveness (a monster attacks players whose combat level is <= 2x its level; a level-14 scorpion is safe from combat level 29) — verified 2026-08-27" - "OSRS wiki — Scorpion (combat level 14, hitpoints 17; Al Kharid mine) — verified 2026-08-27" - - "OSRS wiki — Free-to-play Mining training (ore level/xp table — all rows F2P; pickaxe tier table with tick intervals; Doric's Quest 1,300 xp; iron 50,000 xp/hr at 41+ / 52,500 at 61+; Al Kharid 3-rock spot; SE Varrock banking; Superheat coal; adamantite/runite as F2P money methods; scorpions: 'bring food') — verified 2026-08-27" + - "OSRS wiki — Free-to-play Mining training (ore level/xp table — all rows F2P; pickaxe tier table with tick intervals (bronze 8 -> rune 3); Doric's Quest 1,300 xp; iron 50,000 xp/hr at 41+ / 52,500 at 61+; Al Kharid 3-rock spot; SE Varrock banking; Superheat coal; adamantite/runite as F2P money methods; scorpions noted — the guide says 'bring food', this entry hard-gates the mine to combat 29+ instead, so food is not modeled) — verified 2026-08-27" - "OSRS wiki — Mining (skill overview, members=No)" - "prices.runescape.wiki/api/v1/osrs/mapping (ore ids: iron 440, coal 453, silver 442, gold 444, mithril 447, adamantite 449, runite 451 — all members=false; buy limits)" - "prices.runescape.wiki/api/v1/osrs/latest (ore sell prices, snapshot 2026-08-27)" From 126cf7b38adb667c5a3f46aec28f7b3a7faede1b Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 28 Aug 2026 03:15:04 -0500 Subject: [PATCH 4/7] =?UTF-8?q?codex:=20mining-f2p=20=E2=80=94=20fast-xp/b?= =?UTF-8?q?anked-income=20modes,=20Mining-vs-Attack=20pickaxe=20gates,=20m?= =?UTF-8?q?odeled=20rates=20(bounce-fix=20attempt=203)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Owner decision 2026-08-28: expose separate fast-xp (powermine, drop) and banked-income (deterministic target ore + mine, bank) modes; never select highest-available rocks. Separate pickaxe gates per tier: Mining level decides usability (rune 41), Attack only equipping (rune 40) — pinned 1-Attack/41-Mining carry-and-use example. Modeled policy rates recomputed with cadence, respawn idle, drop/bank legs and breaks (fast-xp ~32,400 XP/hr; banked iron ~56,500 gp/hr); 52,500 kept as labeled external benchmark only. Worked examples per mode covering preparation -> first full inventory -> first break -> resume. Validator now proves the gate distinction and both modes for mining entries. --- codex/training/mining-f2p.yaml | 393 ++++++++++++++++++++++++--------- tools/validate.py | 24 ++ 2 files changed, 309 insertions(+), 108 deletions(-) diff --git a/codex/training/mining-f2p.yaml b/codex/training/mining-f2p.yaml index 22240e3..2dd7445 100644 --- a/codex/training/mining-f2p.yaml +++ b/codex/training/mining-f2p.yaml @@ -3,109 +3,217 @@ title: Mining (F2P) domain: training tags: [mining, gathering, f2p, skill-training] status: verified -version: 3 # 3: bounce-fix 2 2026-08-27 — dynamic inventory/drop model (no hardcoded slot count), conditional equip, wait_rock outcome gate, tier-aware cadence table, Al Kharid hard-gated to combat 29+ (no food in model) -updated: "2026-08-27" # ISO dates must stay quoted — YAML 1.1 parses bare dates to datetime.date +version: 4 # 4: bounce-fix 3 2026-08-28 — owner decision: two explicit run modes (fast-xp powermine / banked-income), separate Mining-use vs Attack-equip pickaxe gates per tier with pinned 1-Attack/41-Mining example, modeled policy rates recomputed (cadence + respawn + drop/bank + breaks; 52,500 kept as labeled external benchmark only), worked examples per mode, validator proves gates + modes +updated: "2026-08-28" # ISO dates must stay quoted — YAML 1.1 parses bare dates to datetime.date summary: > - F2P mining: mine the highest available ore tier with the best pickaxe, - banking the ore (money route) or dropping it (powermine XP route). The - wiki's free-to-play training guide is the authoritative progression: - clay/copper/tin from level 1, iron 15 (the fast-XP ore), silver 20, coal - 30, gold 40, mithril 55, adamantite 70, runite 85. Mining is a staple - low-risk, low-input method with no respawn mechanics in the loop. The Al - Kharid powermine is hard-gated: its level-14 scorpions attack players at - combat level 28 or below (wiki Aggressiveness), so the mine runs only at - combat 29+; sub-29 accounts use the banked South-east Varrock route, - which has no aggressive NPCs and needs no food. + F2P mining with TWO explicit run modes, chosen at session start; the + entry never silently picks "the highest available rock": + + - fast-xp: powermining for XP. Iron (level 15, 35 xp) from level 15 + onward, copper/tin (17.5 xp) below 15; every full inventory is DROPPED + (drop route explicit — drop_run). No banking in the loop. + - banked-income: ONE deterministic target ore + mine fixed at session + start and validated (Mining level >= ore level; the location contains + that ore; the inventory policy can hold it — banked mode deposits every + load); every full inventory is BANKED (bank_run) until the GP target. + + The wiki's free-to-play training guide is the authoritative progression + for fast-xp (clay/copper/tin 1, iron 15, silver 20, coal 30, gold 40, + mithril 55, adamantite 70, runite 85). Mining is a staple low-risk, + low-input method. The Al Kharid powermine is hard-gated: its level-14 + scorpions attack players at combat 28 or below (wiki Aggressiveness), so + that mine runs only at combat 29+; sub-29 accounts use the banked + South-east Varrock route (no aggressive NPCs, no food). Both modes use + one pickaxe policy: choose the best tier by MINING level (usability — + rune is usable at 41 Mining), then equip ONLY when the ATTACK level + permits (rune = 40 Attack), otherwise carry the same selected tool + (mining from the inventory works at any level; a 1-Attack/41-Mining + account carries and uses a rune pickaxe). Worked examples for both modes + live in best_practice.techniques. accounts: membership: f2p # whole ore chain verified F2P via the wiki guide min_level: 1 # clay/copper/tin mineable at level 1 requires_quest: [] requires_other: - - "Best-tier F2P pickaxe the account can wield (bronze: no requirement -> rune: 40 Attack). Equip it ONLY when the Attack requirement is met; otherwise carry it in the inventory — mining from the inventory works at any level (equipped pickaxe = 28 ore slots, carried = 27; slot counts derive dynamically, never hardcoded)" - - "Bank route from the chosen mine (or an inventory-drop spot for powermining)" + - "Pickaxe: select the best tier by MINING level (usability gate: bronze 1, iron 1, steel 6, mithril 21, adamant 31, rune 41); EQUIP only when the ATTACK requirement is met (equip gate: steel 5, mithril 20, adamant 30, rune 40), otherwise CARRY the same selected tool — mining from the inventory works at every level (a 1-Attack/41-Mining account selects, carries, and uses a rune pickaxe; equipped pickaxe frees one slot, carried costs one — slot counts stay dynamic, never hardcoded)" + - "Run mode chosen at session start: fast-xp (powermine, drop every load) or banked-income (deterministic target ore + mine, bank every load)" + - "Bank route from the chosen mine (fast-xp drops instead when no bank leg is wanted)" - "Al Kharid powermine HARD GATE: combat level >= 29 (a monster attacks players at <= 2x its level; the scorpion is level 14, so its aggro ceiling is combat 28 and 29+ is safe). Sub-combat-29 accounts MUST use the banked South-east Varrock route (no aggressive NPCs, no food required)" best_practice: equipment: - - item: highest-tier pickaxe the account can wield (bronze -> rune) - why: "each tier is faster — the roll interval drops from 8 ticks (bronze) to 3 ticks (rune), and the guide recommends always using the highest tier available; equipping it frees an inventory slot. Equip only when the Attack requirement is met (rune = 40 Attack); below that, mine with the pickaxe in the inventory (legal at every level)" - source: "OSRS wiki — Free-to-play Mining training (Pickaxes: tier table with tick intervals)" + - item: "pickaxe: highest tier usable at the account's MINING level (bronze 1, iron 1, steel 6, mithril 21, adamant 31, rune 41)" + why: "each tier is faster — the roll interval drops from 8 ticks (bronze) to 3 ticks (rune), and the guide recommends always using the highest tier available. Two SEPARATE gates: the Mining level decides WHICH pickaxe can be USED, the Attack level decides ONLY whether it can be EQUIPPED (steel 5, mithril 20, adamant 30, rune 40). Equip only when the Attack requirement is met, otherwise carry the same selected tool — a 1-Attack/41-Mining account selects, carries, and uses a rune pickaxe" + source: "OSRS wiki — Free-to-play Mining training (Pickaxes: tier table with tick intervals); OSRS wiki — Rune pickaxe (41 Mining to use, 40 Attack to equip) and Steel/Mithril/Adamant pickaxe pages (use vs equip gates)" techniques: - - name: start with Doric's Quest for 1,300 xp (level 1 to 10) + - name: "mode contract: fast-xp vs banked-income (chosen at session start, never silently highest-available)" + why: "fast-xp powermines iron 15+ (copper/tin below 15) and DROPS every full inventory; banked-income fixes ONE target ore + mine, validates it (Mining level, location, inventory policy), logs it in activity state, and BANKS every load. The two modes pick different rocks, use different inventory handling, and stop at different targets — the entry never selects a rock for the operator" + source: "owner decision 2026-08-28 (governor guidance channel, PR #11): expose separate fast-xp and banked-income modes; encoded in this entry's state_machine + a2 params" + - name: "start with Doric's Quest for 1,300 xp (level 1 to 10)" why: "the quest reward alone takes Mining from 1 to 10; the materials (6 clay, 4 copper, 2 iron) mine at level 1 except iron, which can be bought on the GE" source: "OSRS wiki — Free-to-play Mining training (Level 1-10: Doric's Quest)" - - name: powermine iron at Al Kharid mine for maximum XP (combat 29+ only) - why: "iron (level 15, 35 xp) is the fastest F2P training ore and Al Kharid is the only F2P site with three iron rocks around one tile — zero travel between respawns; ~50,000 xp/hr at 41 Mining with a rune pickaxe, 52,500 at 61+ (3 ores per 12 ticks); drop the ores — banking costs XP. Entry gate: the mine's level-14 scorpions are aggressive to combat 28, so this spot runs at combat 29+" + - name: "powermine iron at Al Kharid mine for maximum XP (fast-xp only, combat 29+ gate)" + why: "iron (level 15, 35 xp) is the fastest F2P training ore and Al Kharid is the only F2P site with three iron rocks around one tile — zero travel between respawns; modeled fast-xp rate ~32,400 XP/hr at 61+ with rune (midpoint policy model, see economy comments); drop the ores, banking costs XP. Entry gate: the mine's level-14 scorpions are aggressive to combat 28, so this spot runs at combat 29+" source: "OSRS wiki — Free-to-play Mining training (Levels 15-99: Iron ore); OSRS wiki — Aggressiveness" - - name: bank iron at the South-east Varrock mine for smithing/money (sub-29 default) - why: "the guide names this F2P banking location explicitly; the eastern Varrock bank is the closest bank leg, and the ores feed Smithing. This is also the sub-combat-29 route (no aggressive NPCs)" + - name: "bank iron at the South-east Varrock mine (sub-29 default and banked-income anchor)" + why: "the guide names this F2P banking location explicitly; the eastern Varrock bank is the closest bank leg, and the ores feed Smithing. Also the sub-combat-29 route (no aggressive NPCs) and the banked-income anchor: modeled ~56,500 gp/hr with rune (midpoint policy model, see economy comments)" source: "OSRS wiki — Free-to-play Mining training (Levels 1/10-15 and 15-99: banking locations)" - - name: "coal route: West Lumbridge Swamp mine, or Superheat in place" - why: "coal (level 30, 50 xp) banks reasonably from the Lumbridge Swamp mine; with 43 Magic, Superheat Item converts coal into bars without a furnace, cutting bank trips (staff of fire + nature runes, 26 inventory slots)" + - name: "banked-income example: adamantite at the Al Kharid mine (70 Mining)" + why: "deterministic target ore + location: adamantite (70 Mining, 95 xp, 584 gp low edge) at the Al Kharid south rocks, banked at the adjacent bank; modeled ~490,000 gp/hr with rune (midpoint policy model, see economy comments); the wiki cites adamantite banking as a good F2P money method" + source: "OSRS wiki — Free-to-play Mining training (Levels 30-99 and money-making); prices.runescape.wiki snapshot 2026-08-27" + - name: "superheat coal (optional banked variant): West Lumbridge Swamp mine" + why: "coal (level 30, 50 xp) banks reasonably from the Lumbridge Swamp mine; with 43 Magic, Superheat Item converts coal into bars without a furnace, cutting bank trips (staff of fire + nature runes)" source: "OSRS wiki — Free-to-play Mining training (Levels 30-99: Superheat Item)" - - name: bank-distance tradeoff - why: "farther mines become viable when bank travel is proportionally small (superheat method) — pick the mine whose bank leg costs the least per trip for the mode being run" - source: "OSRS wiki — Free-to-play Mining training (Levels 30-99: Superheat Item — mines farther from a bank become viable)" + - name: "worked example — fast-xp session (preparation -> first full inventory -> first break -> resume)" + why: "Account: Mining 41, Attack 1, combat 35 (passes the Al Kharid gate). (1) prepare: mode fast-xp; ore policy iron 15+; pickaxe by MINING level -> rune (41 <= 41); equip gate Attack 1 < 40 -> CARRY the same tool (27 ore slots); the rune pickaxe is already owned, so acquire = withdraw from bank; initial inventory = pickaxe + empty ore space. (2) travel: walk to Al Kharid (combat 35 >= 29 OK) and position at the 3-rock cluster. (3) mining rotations: click a fresh iron rock (a2, rune delay [1800, 3600] ms) -> wait_rock: ore gained after the 1.8 s roll -> mine; repeat until the inventory is full. (4) first full-inventory boundary: 27 ores -> drop_run: 27 executions of a6 (right_click Drop, [200, 800] ms each) -> count_item ore == 0 -> mine. (5) first scheduled break: after the 8th-14th rotation the break counter trips -> break_state: take_break 60-180 s (a10). (6) resume: reposition at the cluster (a11) -> mine; the loop repeats until the XP target or session cap -> complete" + source: "derived from this entry's policy model (state_machine prepare/travel/mine/wait_rock/drop_run/break_state/complete + actions a1/a2/a6/a8-a11 + run_control.break), synthesized 2026-08-28" + - name: "worked example — banked-income session (preparation -> first full inventory -> first break -> resume)" + why: "Account: Mining 70, Attack 40, combat 60. Target: 500,000 gp banked or a 2 h session cap. (1) prepare: mode banked-income; target ore = adamantite (ore level 70 <= Mining 70 OK; location = Al Kharid south rocks, which contain adamantite; inventory policy = banked, deposit every load) — target logged in activity state; pickaxe by MINING level -> rune (41 <= 70); equip gate Attack 40 >= 40 -> EQUIP (28 ore slots); tool owned, withdraw from bank. (2) travel: walk to the Al Kharid south adamantite spot. (3) mining rotations: click a fresh adamantite rock (rune roll 1.8 s, delay [1800, 3600] ms) -> wait_rock: ore gained -> mine; repeat. (4) first full-inventory boundary: 28 ores -> bank_run: walk to the adjacent Al Kharid bank (a3), deposit all 28 (a4; load value 28 x 584 = 16,352 gp logged to activity state), walk back (a5) -> mine. (5) first scheduled break: after 8-14 rotations -> break_state: take_break 60-180 s (independent of the bank trips). (6) resume: reposition at the spot (a11) -> mine; complete when the banked value >= 500,000 gp or the session cap hits" + source: "derived from this entry's policy model (state_machine prepare/travel/mine/wait_rock/bank_run/break_state/complete + actions a1-a5/a8-a11 + run_control.break), synthesized 2026-08-28" economy: - # Snapshot 2026-08-27, prices.runescape.wiki latest (sell = low edge). - # Cycle = one rock mined -> one ore (XP path) or one ore banked (money path). - # XP rates sourced from the wiki guide (iron powermine); gp/h model below is - # derived and labelled (no wiki gp/hr figure exists for banked iron). - # Cadence consistency: the 52,500 xp/hr figure IS the continuous 61+ rotation - # (rune pickaxe): 52,500 / 35 xp = 1,500 ores/hr = one ore per 2.4s, which is - # the rune roll (1.8s) + wait_rock outcome gate + anti_ban delay [1800, 3600] - # midpoint (2.7s) — the modeled a2 cadence brackets the wiki rate. - item: Iron ore # anchor: the F2P training ore + # Anchor: iron ore (the fast-xp training ore). Sell prices are the + # 2026-08-27 snapshot (prices.runescape.wiki latest, low edge). + # + # MODELLED POLICY RATES (midpoint model, derived 2026-08-28) — the + # entry's OWN parameters, not the wiki's zero-overhead ideals: + # per-ore cycle = a2 anti_ban.delay midpoint (rune [1800, 3600] ms -> + # 2.7 s; the governor ceiling check 3600/2.7 x 35 = 46,667 XP/hr is an + # unreachable ideal even before overhead) + 10% respawn/depletion idle + # (wait_rock absorbs the wiki 3-12 s rock respawn) + drop-or-bank leg + # amortised per ore + break amortised per ore (120 s per 11 rotations, + # run_control.break midpoints). + # fast-xp iron, drop, rune 61+ (equipped): 2.7 + 0.3 + 0.5 (28 drops x + # 0.5 s per load / 28 ores) + 0.39 (120/11/28) = 3.89 s/ore -> ~925 + # ores/hr -> ~32,400 XP/hr (xp_per_hour below). + # banked-income iron, SE Varrock: 2.7 + 0.3 + 1.07 (bank leg ~30 s per + # 28-ore load) + 0.39 = 4.46 s/ore -> ~807 ores/hr -> ~56,500 gp/hr + # (gp_per_hour_high below). + # banked-income adamantite, Al Kharid south: 2.7 + 0.3 + 0.9 (short + # bank leg ~25 s per load) + 0.39 = 4.29 s/ore -> ~839 ores/hr -> + # ~490,000 gp/hr (profit_drivers). The 2-rock spot respawns tighter + # than the 3-rock spot, so treat modeled rates as upper bounds. + # bronze path (below 15, copper/tin 17.5 xp): cadence midpoint 5.7 s -> + # ~8,800 XP/hr. + # EXTERNAL BENCHMARK (NOT the modeled rate): the wiki's 50,000 XP/hr + # (41+) / 52,500 XP/hr (61+) iron powermine figures assume a continuous + # 3-tick rotation with zero delay/drop/break overhead; the configured + # midpoint cadence alone caps that ideal at ~46,667 XP/hr, so 52,500 is + # cited ONLY as a labeled wiki benchmark and never as this entry's + # expected output. + item: Iron ore # fast-xp anchor; banked-income targets in profit_drivers item_id: 440 cost_per_cycle: 0 # mining consumes nothing (pickaxe has no fuel; no supply costs) income_per_cycle: 70 # iron ore sell price (low edge), snapshot 2026-08-27 - net_per_cycle: 70 # no per-cycle costs; selling optional (powermine drops -> 0) - gp_per_hour_low: 0 # powermine mode drops ores — XP is the product - gp_per_hour_high: 28000 # MODELLED (dated): ~400 banked iron ores/hr (bank legs reduce the 52.5k-xp/hr powermine rate) x 70 gp - xp_per_hour: 52500 # iron powermine, 61+ (50,000 at 41+ with rune pickaxe) — wiki guide + net_per_cycle: 70 # no per-cycle costs; fast-xp drops ores -> realized income 0 + gp_per_hour_low: 0 # fast-xp mode drops every load — XP is the product + gp_per_hour_high: 56500 # MODELLED banked-income rate (iron @ SE Varrock, midpoint model above) + xp_per_hour: 32400 # MODELLED fast-xp policy rate (rune 61+ drop, midpoint model above) — NOT the 52,500 wiki benchmark supplies: - - item: "pickaxe: highest tier the account can wield (rune needs 40 Attack to equip; below that carried in the inventory)" + - item: "pickaxe — best tier selectable by MINING level (bronze 1, iron 1, steel 6, mithril 21, adamant 31, rune 41); equip ONLY when the ATTACK level permits (steel 5, mithril 20, adamant 30, rune 40), otherwise carry the same selected tool (a 1-Attack/41-Mining account carries and uses a rune pickaxe)" qty_per_cycle: 1 source: bank profit_drivers: - "F2P ore chain (wiki guide): clay 1/5xp, copper 1/17.5, tin 1/17.5, iron 15/35, silver 20/40, coal 30/50, gold 40/65, mithril 55/80, adamantite 70/95, runite 85/125" - "Sell prices 2026-08-27 (low edge): iron 70, coal 151, silver 73, gold 154, mithril 159, adamantite 584, runite 10,265" - - "Money routes: adamantite banking is a good F2P money method (70 Mining); runite (85 Mining) is cited as one of the fastest F2P money-making methods" - - "For XP, powermining iron beats every banked route — drop the ores (guide-recommended)" + - "fast-xp: powermine iron 15+ and drop every load — XP is the product, realized gp 0" + - "banked-income (midpoint model, derived 2026-08-28): iron @ SE Varrock ~56,500 gp/hr; adamantite @ Al Kharid south ~490,000 gp/hr — deterministic target ore + location, never highest-available" + - "Money routes (wiki): adamantite banking is a good F2P money method (70 Mining); runite (85 Mining) is one of the fastest F2P money-making methods" state_machine: - initial: idle + initial: prepare terminal: [complete, failed] states: - idle: - goal: "be at the chosen mine with the pickaxe ready (equipped only if the account meets its Attack requirement) and the inventory clear; run mode chosen at session start (powermine: drop ores; banked: bank ores)" - next: [mine, complete, failed] + prepare: + goal: > + Session start. 1) Choose the run mode (session param): fast-xp + (powermine, drop) or banked-income (deterministic target ore + + location, bank). 2) fast-xp ore policy: iron 15+ (copper/tin below + 15). banked-income: pick ONE target ore and ONE mine now and + validate: level_is Mining >= ore level, the location contains that + ore, and the inventory policy can hold it (banked -> deposit every + load). 3) Pickaxe: select the highest tier whose MINING + requirement the account meets (bronze 1, iron 1, steel 6, mithril + 21, adamant 31, rune 41 — rune is usable at 41 Mining and + equipable at 40 Attack; a 1-Attack/41-Mining account selects and + CARRIES a rune pickaxe). 4) Equip ONLY when level_is Attack >= + the tier's equip requirement (steel 5, mithril 20, adamant 30, + rune 40); otherwise carry the same selected tool. 5) Acquire the + tool if missing (a8) and build the initial inventory: pickaxe + (equipped or carried) + empty ore space; no food (both routes are + gate-safe). 6) Record mode + target in activity state — the target + must be visible, never implicit. + next: [travel, complete, failed] + travel: + goal: "walk/teleport to the selected mine (fast-xp: Al Kharid 3-rock powermine at combat 29+, else banked South-east Varrock; banked-income: the session's validated target mine) and position at the target cluster; arrive with the pickaxe equipped or carried per the prepare gate" + next: [mine, recover] mine: - goal: "mine the current tier's rock until the inventory is full or the target is reached; every click (a2) is followed by the wait_rock outcome wait — never chain clicks on the same rock" - next: [wait_rock, drop_run, bank_run, complete, failed] + goal: > + Mine the session target rock until the inventory is full or the + run target is reached. Rock selection is MODE-DRIVEN and + deterministic: fast-xp mines iron (15+) or copper/tin (<15); + banked-income mines ONLY the session target ore (never the highest + available rock). Every a2 click is followed by the wait_rock + outcome wait — never chain clicks on the same rock. + next: [wait_rock, drop_run, bank_run, recover, survive, complete] wait_rock: - goal: "after clicking a rock (a2), wait for the mining outcome — ore gained, rock depleted, or roll timeout — before any re-click; never re-click the active rock while its roll is unresolved" - next: [mine, drop_run, bank_run, failed] - bank_run: - goal: "walk to the bank, deposit ores, walk back to the mine" - next: [mine, idle] + goal: "after clicking a rock (a2), wait for the mining outcome — ore gained, rock depleted, or roll timeout (2x the equipped tier's roll) — before any re-click; never re-click the active rock while its roll is unresolved; a fully depleted cluster is a respawn wait, absorbed here (wiki rock respawn 3-12 s)" + next: [mine, drop_run, bank_run, recover] drop_run: - goal: "drop every occupied ore slot: one right-click Drop per slot (a6), looping until the inventory holds no ore — slot count is DYNAMIC (inventory capacity minus non-ore items: equipped pickaxe = 28 ore slots, carried = 27, survival/gear load = fewer); drive the loop off count_item ore, never hardcode a slot number" - next: [drop_run, mine] + goal: "fast-xp ONLY: drop every occupied ore slot — one right-click Drop per slot (a6 re-executed), looping until count_item ore == 0. Slot count is DYNAMIC: inventory capacity minus non-ore items (equipped pickaxe = 28 ore slots, carried = 27, any session load = fewer) — never a hardcoded loop count" + next: [drop_run, mine, break_state, complete] + bank_run: + goal: "banked-income ONLY: walk to the nearest bank, deposit all ores (activity state: target ore + load value logged), walk back to the mine; the pickaxe stays (equipped or carried) and the inventory is rebuilt as empty ore space for the next load" + next: [mine, break_state, complete] + break_state: + goal: "scheduled break independent of incidental banking: after every run_control.break.every rotations (8-14) take_break (60-180 s AFK, a10), then reposition at the cluster (a11) and resume mining" + next: [mine, complete] + recover: + goal: "diagnose and fix: no fresh rock of the target tier for 2 respawn cycles (rotate cluster / wait respawn); invalid location (is_at fails — wrong mine or wrong ore) -> re-travel to the correct mine; missing tool (has_item pickaxe false) -> acquire (a8) and re-prepare; path failure (move_to on_fail) -> retry travel. Persistent after 3 recoveries -> failed" + next: [prepare, travel, mine, failed] survive: goal: "defensive fallback only (the 29+ gate means scorpions never aggro at Al Kharid): if attacked anyway (gate bypassed/griefed), retreat out of the aggro radius and re-enter the cluster when safe; no food loop is modeled" next: [mine, failed] complete: - goal: level or GP target reached + goal: "fast-xp: XP/level target reached. banked-income: banked GP target reached (or session cap)." failed: - goal: hard failure — stop, log ERROR sentinel, alert + goal: "hard failure — stop, log ERROR sentinel, alert" transitions: + - from: prepare + when: "mode + target set and validated, tool ready, inventory built" + to: travel + - from: prepare + when: "target already reached at session start (fast-xp level target / banked GP target)" + to: complete + - from: prepare + when: "3 consecutive failed tool acquisitions or an unvalidatable target (invalid banked ore/location combination)" + to: failed + - from: travel + when: "is_at the selected mine and positioned at the cluster" + to: mine + - from: travel + when: "path failure (move_to retries exhausted)" + to: recover - from: mine when: "a2 rock click issued (the tier roll is now in progress)" to: wait_rock + - from: mine + when: "inventory full (fast-xp mode)" + to: drop_run + - from: mine + when: "inventory full (banked-income mode)" + to: bank_run + - from: mine + when: "no fresh target-tier rock available for 2 respawn cycles, or has_item pickaxe == false (tool lost/griefed)" + to: recover + - from: mine + when: "HP < 40% (hp_is — defensive only; unexpected aggro despite the gate)" + to: survive + - from: mine + when: "fast-xp: XP/level target reached; banked-income: banked GP target reached (or session cap)" + to: complete - from: wait_rock when: "ore gained (count_item ore > count at click) or the clicked rock depleted" to: mine @@ -113,52 +221,70 @@ state_machine: when: "roll timeout (no outcome within 2x the equipped tier's roll interval)" to: mine - from: wait_rock - when: "inventory full (powermine mode)" + when: "inventory full (fast-xp mode)" to: drop_run - from: wait_rock - when: "inventory full (banked mode)" - to: bank_run - - from: mine - when: "inventory full (powermine mode)" - to: drop_run - - from: mine - when: "inventory full (banked mode)" + when: "inventory full (banked-income mode)" to: bank_run + - from: wait_rock + when: "cluster fully depleted and the respawn wait exceeded (no rock of the target tier in range)" + to: recover - from: drop_run when: "inventory still holds ore (count_item ore > 0)" to: drop_run - from: drop_run - when: "inventory empty of ore (count_item ore == 0)" + when: "inventory empty of ore (count_item ore == 0) and break counter < break.every" to: mine - - from: mine - when: "HP < 40% (hp_is — defensive only; unexpected aggro despite the gate)" - to: survive + - from: drop_run + when: "inventory empty of ore (count_item ore == 0) and break counter >= break.every" + to: break_state + - from: drop_run + when: "fast-xp XP target reached (target < inventory capacity)" + to: complete + - from: bank_run + when: "ores deposited and back at the mine, break counter < break.every" + to: mine + - from: bank_run + when: "ores deposited and back at the mine, break counter >= break.every" + to: break_state + - from: bank_run + when: "banked GP target reached on this deposit" + to: complete + - from: break_state + when: "take_break finished (60-180 s)" + to: mine + - from: break_state + when: "session cap reached during the break" + to: complete + - from: recover + when: "tool missing -> re-acquired (a8) or withdrawn from bank" + to: prepare + - from: recover + when: "wrong location -> re-routed to the correct mine" + to: travel + - from: recover + when: "rocks unavailable -> respawned or rotated to a fresh cluster of the target tier" + to: mine + - from: recover + when: "recovery looped 3 times (persistent tool/location/path failure)" + to: failed - from: survive when: "out of combat (HP stable, no attacker in range)" to: mine - from: survive when: "is_dead (gate bypassed/griefed)" to: failed - - from: bank_run - when: "ores deposited and back at the mine" - to: mine - - from: idle - when: "level or GP target reached" - to: complete - - from: mine - when: "level or GP target reached" - to: complete actions: - id: a1 - step: reach-mine + step: travel-to-mine verb: move_to - target: "selected F2P mine" + target: "the mode's mine" params: - location: "Al Kharid Mine (powermine) or South-east Varrock mine (banked)" + location: "fast-xp: Al Kharid Mine powermine (combat 29+ only) or South-east Varrock mine (sub-29 default); banked-income: the session's validated target mine (example: Al Kharid south adamantite rocks)" method: [walk, teleport] - note: "arrive with the pickaxe equipped ONLY if the account meets its Attack requirement (rune = 40 Attack); otherwise carry it in the inventory" - route_gate: "Al Kharid powermine ONLY at combat level >= 29 (level_is Combat >= 29 — the level-14 scorpion's aggro ceiling is combat 28, wiki Aggressiveness); below combat 29 use the banked South-east Varrock route (no aggressive NPCs, no food required)" + note: "arrive with the pickaxe equipped ONLY if level_is Attack >= the tier's equip requirement (rune = 40 Attack); otherwise carry the same selected tool in the inventory" + route_gate: "Al Kharid powermine ONLY at combat level >= 29 (level_is Combat >= 29 — the level-14 scorpion's aggro ceiling is combat 28, wiki Aggressiveness); below combat 29 use the banked South-east Varrock route (no aggressive NPCs, no food required); the Al Kharid south adamantite spot sits outside the scorpion cluster and is gate-checked the same way" on_fail: retry anti_ban: delay: [400, 1600] @@ -166,42 +292,44 @@ actions: - id: a2 step: mine-rock verb: click_object - target: "fresh rock in the cluster — never the rock whose roll is in progress" + target: "fresh rock of the session target tier — never the rock whose roll is in progress" params: - rock: "iron rock (tier-gated: silver 20, coal 30, gold 40, mithril 55, adamantite 70, runite 85)" - gate: "level_is Mining >= rock level" - rotation: "3-rock cluster (Al Kharid) or nearest cluster of the selected tier; face the respawning rock and click a fresh rock only after the wait_rock outcome wait" - roll_table: "pickaxe roll interval (ticks x 0.6s, wiki table verified 2026-08-27): bronze 8 = 4.8s, iron 7 = 4.2s, steel 6 = 3.6s, mithril 5 = 3.0s, adamant 4 = 2.4s, rune 3 = 1.8s" - cadence: "mandatory pause per click = equipped pickaxe roll interval from roll_table + [0, 1800]ms humanisation (anchor model below: rune pickaxe @ 61+ -> delay [1800, 3600]; bronze users apply [4800, 6600], iron [4200, 6000], steel [3600, 5400], mithril [3000, 4800], adamant [2400, 4200]). The pause must never be shorter than the equipped tier's FULL roll, and the wait_rock outcome wait must elapse before any re-click — clicking before the roll resolves (or before the rock depletes) cannot speed the ore and is the bot tell" + rock: "MODE-DRIVEN: fast-xp -> iron rock (level 15+; copper/tin below 15); banked-income -> ONLY the session target ore validated in prepare (silver 20, coal 30, gold 40, mithril 55, adamantite 70, runite 85) — never highest-available" + gate: "level_is Mining >= rock level (e.g. adamantite = 70), and for banked-income the rock must be at the session's validated location" + mode_visible: "activity state logs mode + target ore (id + name) on every rotation — the target is visible, never implicit" + rotation: "3-rock cluster (Al Kharid) or the nearest cluster of the target tier; face the respawning rock and click a fresh rock only after the wait_rock outcome wait" + roll_table: "pickaxe roll interval (ticks x 0.6 s, wiki table verified 2026-08-27): bronze 8 = 4.8 s, iron 7 = 4.2 s, steel 6 = 3.6 s, mithril 5 = 3.0 s, adamant 4 = 2.4 s, rune 3 = 1.8 s" + cadence: "mandatory pause per click = the equipped tier's full roll interval from roll_table + [0, 1800] ms humanisation: rune [1800, 3600], adamant [2400, 4200], mithril [3000, 4800], steel [3600, 5400], iron [4200, 6000], bronze [4800, 6600]. The pause must never be shorter than the equipped tier's FULL roll, and the wait_rock outcome wait must elapse before any re-click — clicking before the roll resolves cannot speed the ore and is the bot tell" wait_for: "mining outcome — modeled as the wait_rock state: re-click a fresh rock only when ore was gained, the clicked rock depleted, or the roll timed out (2x roll)" on_fail: retry anti_ban: - delay: [1800, 3600] # rune-pickaxe anchor (61+): full 1.8s roll + up to 1.8s humanisation; per-tier floor in params.roll_table — never below the equipped tier's full roll + delay: [1800, 3600] # rune-pickaxe anchor (61+): full 1.8 s roll + up to 1.8 s humanisation; per-tier floor in params.roll_table/cadence — never below the equipped tier's full roll - id: a3 - step: bank-ores + step: bank-approach verb: move_to target: bank params: - location: "nearest bank to the mine (east Varrock for SE Varrock mine, Lumbridge Castle for the Swamp mine)" + location: "nearest bank to the mine (Al Kharid bank for the powermine/adamantite spot, east Varrock bank for SE Varrock mine, Lumbridge Castle for the Swamp mine)" method: [walk, teleport] on_fail: retry anti_ban: delay: [400, 1200] - id: a4 - step: deposit + step: deposit-ores verb: bank_inventory target: bank params: deposit: [all ores] - withdraw: "nothing (banked mode)" + withdraw: "nothing (banked-income mode; the pickaxe stays equipped or carried — inventory rebuilt as empty ore space for the next load)" + note: "log the load to activity state: target ore, qty, banked value" on_fail: alert anti_ban: delay: [500, 1100] - id: a5 - step: return-mine + step: return-to-mine verb: move_to target: "the mine" params: @@ -211,13 +339,13 @@ actions: delay: [400, 1200] - id: a6 - step: drop-ores + step: drop-ore-slot verb: right_click target: "ore slot in inventory" params: option: "Drop" - iterate: "one occupied ore slot per execution — non-stackable ores need one Drop per slot; the drop_run -> drop_run transition re-executes a6 until count_item ore == 0. Slot count is DYNAMIC (inventory capacity minus non-ore items: equipped pickaxe = 28, carried = 27) — never hardcode a slot number" - mode: "powermine only" + iterate: "one occupied ore slot per execution — non-stackable ores need one Drop per slot; drop_run -> drop_run re-executes a6 until count_item ore == 0. Slot count is DYNAMIC (inventory capacity minus non-ore items: equipped pickaxe = 28 ore slots, carried = 27, any session load = fewer) — never a hardcoded loop count" + mode: "fast-xp only — banked-income never drops ore" on_fail: skip anti_ban: delay: [200, 800] # drop churn on a full inventory is human-normal @@ -233,32 +361,81 @@ actions: anti_ban: delay: [600, 1800] + - id: a8 + step: acquire-tool + verb: ge_buy + target: grand-exchange + params: + item: "the selected pickaxe tier (Mining-gated: bronze 1, iron 1, steel 6, mithril 21, adamant 31, rune 41)" + qty: 1 + max_price: "GE mid + 10% cap" + note: "acquisition route for prepare/recover: when the tool is already owned, withdraw it from the bank instead (bank_inventory withdraw 1x pickaxe); buy only what is missing — never a second pickaxe in the inventory" + on_fail: retry + anti_ban: + delay: [1200, 3000] + + - id: a9 + step: equip-tool + verb: use_item_on + target: "self" + params: + item: "the selected pickaxe tier" + on: "self (equip)" + gate: "RUN ONLY IF level_is Attack >= the tier's equip requirement (steel 5, mithril 20, adamant 30, rune 40) — otherwise SKIP and carry the same selected tool in the inventory; mining from the inventory works at any level (a 1-Attack/41-Mining account mines with a carried rune pickaxe)" + on_fail: skip + anti_ban: + delay: [600, 1400] + + - id: a10 + step: take-break + verb: take_break + target: "self" + params: + duration: "run_control.break.for [60, 180] s of idle AFK" + note: "breaks are independent of incidental banking: the counter runs on rotations (run_control.break.every [8, 14]), not on bank visits" + on_fail: retry + anti_ban: + delay: [300, 900] + + - id: a11 + step: reposition-cluster + verb: walk_to + target: "cluster center / fresh rock of the target tier" + params: + location: "the session's mine, cluster of the target tier" + note: "recovery + post-break reposition: after take_break, respawn waits, or a recover diagnosis, walk to the cluster's centre tile and re-select a fresh rock; never click a rock while walking" + on_fail: retry + anti_ban: + delay: [400, 1200] + run_control: - iteration: "one full mine -> bank (or drop) rotation" # per docs/schema.md: break.every counts iterations + iteration: "one full mine -> drop (fast-xp) or mine -> bank (banked-income) rotation" max_iterations: null break: - every: [8, 14] # iterations = full mine->bank/drop rotations - for: [60, 180] # seconds of idle AFK + every: [8, 14] # rotations; the break counter is independent of incidental banking + for: [60, 180] # seconds of idle AFK (take_break, a10) stop_conditions: - - "level or GP target reached" + - "fast-xp: Mining XP/level target reached (session target); banked-income: banked GP target reached or session cap" - "3 consecutive failed rock clicks (client issue — do not loop)" - - "death: hard failure — the Al Kharid hard gate (combat 29+) and the survive (retreat) fallback prevent it in normal operation; if is_dead anyway (gate bypassed/griefed) log ERROR + alert and stop, no respawn loop" + - "recover looped 3 times: persistent missing tool, invalid location, or path failure" + - "death: hard failure — the Al Kharid hard gate (combat 29+) and the survive (retreat) fallback prevent it in normal operation; if is_dead anyway (gate bypassed/griefed) log ERROR + alert and stop — no respawn loop" risk: ban_level: low notes: - "Mining is a staple low-risk method: long, repetitive, low-input behaviour that matches normal player sessions — the guide itself recommends hours of drop-mining." - - "The powermine rotation is the detection vector if executed perfectly for hours — keep the a2 tier-aware cadence (wait_rock outcome wait + roll_table pause; anchor rune 1.8-3.6s, never below the equipped tier's full roll) and take the break schedule." - - "Al Kharid mine has aggressive level-14 scorpions (they attack players at combat level <= 28 — wiki Aggressiveness). The entry HARD-gates Al Kharid to combat 29+; sub-29 accounts run the banked South-east Varrock route (no aggressive NPCs). No food is part of either route; the survive state (retreat) is a defensive fallback only." + - "The powermine rotation is the detection vector if executed perfectly for hours — keep the a2 tier-aware cadence (wait_rock outcome wait + roll_table pause; anchor rune 1.8-3.6 s, never below the equipped tier's full roll) and take the break schedule." + - "Al Kharid mine has aggressive level-14 scorpions (they attack players at combat level <= 28 — wiki Aggressiveness). The entry HARD-gates Al Kharid to combat 29+; sub-29 accounts run the banked South-east Varrock route (no aggressive NPCs). Both modes select the pickaxe by Mining level and equip only when the Attack level permits (rune 41 Mining usable / 40 Attack equip: a 1-Attack/41-Mining account carries and uses a rune pickaxe). No food is part of either route; the survive state (retreat) is a defensive fallback only." sources: - "OSRS wiki — Active/passive aggressive monsters: Aggressiveness (a monster attacks players whose combat level is <= 2x its level; a level-14 scorpion is safe from combat level 29) — verified 2026-08-27" - "OSRS wiki — Scorpion (combat level 14, hitpoints 17; Al Kharid mine) — verified 2026-08-27" - - "OSRS wiki — Free-to-play Mining training (ore level/xp table — all rows F2P; pickaxe tier table with tick intervals (bronze 8 -> rune 3); Doric's Quest 1,300 xp; iron 50,000 xp/hr at 41+ / 52,500 at 61+; Al Kharid 3-rock spot; SE Varrock banking; Superheat coal; adamantite/runite as F2P money methods; scorpions noted — the guide says 'bring food', this entry hard-gates the mine to combat 29+ instead, so food is not modeled) — verified 2026-08-27" + - "OSRS wiki — Free-to-play Mining training (ore level/xp table — all rows F2P; pickaxe tier table with tick intervals (bronze 8 -> rune 3); Doric's Quest 1,300 xp; iron 50,000 xp/hr at 41+ / 52,500 at 61+ cited as zero-overhead external benchmarks only; Al Kharid 3-rock spot; SE Varrock banking; Superheat coal; adamantite/runite as F2P money methods; scorpions noted — this entry hard-gates the mine to combat 29+ instead, so food is not modeled) — verified 2026-08-27" - "OSRS wiki — Mining (skill overview, members=No)" + - "OSRS wiki — Rune pickaxe (41 Mining to use, 40 Attack to equip) / Adamant pickaxe (31 Mining, 30 Attack) / Mithril pickaxe (21 Mining, 20 Attack) / Steel pickaxe (6 Mining, 5 Attack) — use gate vs equip gate per tier — verified 2026-08-28" - "prices.runescape.wiki/api/v1/osrs/mapping (ore ids: iron 440, coal 453, silver 442, gold 444, mithril 447, adamantite 449, runite 451 — all members=false; buy limits)" - "prices.runescape.wiki/api/v1/osrs/latest (ore sell prices, snapshot 2026-08-27)" -verified: "2026-08-27" # keep quoted (YAML 1.1 date parsing) +verified: "2026-08-28" # keep quoted (YAML 1.1 date parsing) related: - magic-alch-handoff diff --git a/tools/validate.py b/tools/validate.py index 7667c3d..afcc8e9 100644 --- a/tools/validate.py +++ b/tools/validate.py @@ -125,6 +125,30 @@ def main(): f"{rel}: best_practice '{line.get('item') or line.get('name')}' " f"carries a 'researched' source — citation TODO, file follow-up") + # 4. pickaxe gate distinction + mining run-mode contract + # (owner guidance 2026-08-28): the Mining level decides WHICH + # pickaxe can be USED; the Attack level decides ONLY whether it + # can be EQUIPPED. Any entry modelling pickaxes must express both + # gates separately; a mining entry must additionally pin the + # 1-Attack/41-Mining carry-and-use example (rune pickaxe usable at + # 41 Mining, equipable only at 40 Attack) and expose BOTH run + # modes (fast-xp powermine / banked-income). + blob = (yaml.safe_dump(data, sort_keys=False) or "").lower() + if "pickaxe" in blob: + if "mining level" not in blob: + errors.append(f"{rel}: pickaxe model misses the USABILITY gate — " + f"Mining level decides which pickaxe can be used") + if "attack" not in blob: + errors.append(f"{rel}: pickaxe model misses the EQUIP gate — " + f"Attack decides only whether the pickaxe can be equipped") + if "mining" in eid: + if "1-attack/41-mining" not in blob: + errors.append(f"{rel}: pickaxe gates lack the pinned 1-Attack/41-Mining " + f"example (a rune pickaxe carried and used at 1 Attack / 41 Mining)") + if "fast-xp" not in blob or "banked-income" not in blob: + errors.append(f"{rel}: mining entry must expose BOTH run modes " + f"(fast-xp powermine and banked-income) — owner decision 2026-08-28") + # report for w in warnings: print(f"WARN {w}") From 765505ed0fb334c56486fbc97799e9fd146a1c1a Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 28 Aug 2026 03:34:48 -0500 Subject: [PATCH 5/7] codex: structured mining data model + respawn/success-correct rates + executable tool prep (bounce-fix 4, owner mode decision) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Governor bounce 4 + owner guidance 2026-08-28 (structured, provable, full lifecycle): 1. site model is data, not prose: new mine_sites table (ore, gates, rock_count, respawn_seconds, rolls_per_ore, modeled_ores_per_hour, world_hop, selectable, inventory_policy, access, safety, route). Adamantite @ Al Kharid south is now respawn-BOUND (2 rocks, 240 s respawn): modeled 120 ores/hr ONLY with world hopping (wiki F2P adamantite guide) = 70,080 gp/hr; no-hop ceiling ~30/hr = 17,520; the 839 ores/hr iron-cadence fantasy is gone and a cadence-ceiling gate rejects it. Runite is selectable:false (Wilderness PK risk, ~12 min respawn) — refused, not hand-waved. New hop state + hop_world action (a14) implement the world-hop policy; long-respawn selectable sites REQUIRE world_hop + a hop_world action (validator). 2. fast-xp success model: rolls_per_ore 1.33 (61+, derived from the wiki 52,500 benchmark: 1,500 ores/hr / 2,000 3-tick rolls/hr); rates recomputed with success rolls + respawn math (3 rocks x 3.59 s = 10.8 s > 5.4 s respawn -> zero idle, pinned) + drop/bank + breaks: xp_per_hour 28,000 (was 32,400 with a flat 0.3 s and 1 ore/click); 52,500 stays a labeled external benchmark. 3. sub-29 fast-xp preserved: South-east Varrock DROP site (safe, same mode) — the mode never silently switches to banking. 4. tool prep executable end to end: check-tool (has_item) -> withdraw-tool (owned branch, bank_inventory) or buy-tool + wait-ge-fill (ge_offers_done) + collect-tool (ge_collect, verify) -> equip-tool via menu_option Wield with the Attack gate; recovery on purchase/equip failure (3 -> failed). 5. VALIDATOR PROVES THE CONTRACT (no phrase-search): pickaxe_tiers must match the canonical F2P table exactly (Mining=use gate, Attack=equip gate, roll_ticks); pickaxe_examples recomputed per row (1-Attack/41-Mining must select + CARRY rune; 40-Attack/70-Mining equips; 5-Attack/30-Mining carries mithril); both modes declared with fixed inventory policies; mode refs must resolve to declared selectable policy-matching sites; Wilderness selectable -> error; modeled ores/hr <= 3600/(rolls_per_ore x 2.7 s); xp_per_hour within 2% of the modeled fast-xp rate. 13 committed --selftest fixtures (negative cases included) wired into every gate run. Evidence: validate 2 entries 0 errors 1 pre-existing WARN; --selftest 13 PASS; synthetic 3-way merge main 79cab78 (incl. catalog) + head clean + gate green; git diff --check clean. version 4 -> 5. --- codex/training/mining-f2p.yaml | 493 ++++++++++++++++++++++++--------- schemas/entry.schema.json | 91 ++++++ tools/validate.py | 393 ++++++++++++++++++++++++-- 3 files changed, 815 insertions(+), 162 deletions(-) diff --git a/codex/training/mining-f2p.yaml b/codex/training/mining-f2p.yaml index 2dd7445..1cff11f 100644 --- a/codex/training/mining-f2p.yaml +++ b/codex/training/mining-f2p.yaml @@ -3,175 +3,327 @@ title: Mining (F2P) domain: training tags: [mining, gathering, f2p, skill-training] status: verified -version: 4 # 4: bounce-fix 3 2026-08-28 — owner decision: two explicit run modes (fast-xp powermine / banked-income), separate Mining-use vs Attack-equip pickaxe gates per tier with pinned 1-Attack/41-Mining example, modeled policy rates recomputed (cadence + respawn + drop/bank + breaks; 52,500 kept as labeled external benchmark only), worked examples per mode, validator proves gates + modes +version: 5 # 5: bounce-fix 4 2026-08-28 — structured mine_sites/pickaxe_tiers/pickaxe_examples/mining_modes data model (validated exactly by the validator: no phrase-search); per-site respawn/depletion modeling (adamantite = respawn-bound + world-hop policy, runite NOT selectable — Wilderness PK risk); success-model rolls_per_ore; executable tool prep (check/withdraw/buy/wait/collect/equip via Wield); hop_world action; sub-29 fast-xp stays fast-xp at a safe drop site; modeled rates recomputed (28,000 xp/hr fast-xp drop; 70,080 gp/hr adamantite w/ hop; 49,900 iron banked) updated: "2026-08-28" # ISO dates must stay quoted — YAML 1.1 parses bare dates to datetime.date summary: > - F2P mining with TWO explicit run modes, chosen at session start; the - entry never silently picks "the highest available rock": - + F2P mining with TWO explicit run modes, chosen at session start and + validated against the structured mine_sites table; the entry never + silently picks "the highest available rock": - fast-xp: powermining for XP. Iron (level 15, 35 xp) from level 15 onward, copper/tin (17.5 xp) below 15; every full inventory is DROPPED - (drop route explicit — drop_run). No banking in the loop. - - banked-income: ONE deterministic target ore + mine fixed at session - start and validated (Mining level >= ore level; the location contains - that ore; the inventory policy can hold it — banked mode deposits every - load); every full inventory is BANKED (bank_run) until the GP target. - - The wiki's free-to-play training guide is the authoritative progression - for fast-xp (clay/copper/tin 1, iron 15, silver 20, coal 30, gold 40, - mithril 55, adamantite 70, runite 85). Mining is a staple low-risk, - low-input method. The Al Kharid powermine is hard-gated: its level-14 - scorpions attack players at combat 28 or below (wiki Aggressiveness), so - that mine runs only at combat 29+; sub-29 accounts use the banked - South-east Varrock route (no aggressive NPCs, no food). Both modes use - one pickaxe policy: choose the best tier by MINING level (usability — - rune is usable at 41 Mining), then equip ONLY when the ATTACK level - permits (rune = 40 Attack), otherwise carry the same selected tool - (mining from the inventory works at any level; a 1-Attack/41-Mining - account carries and uses a rune pickaxe). Worked examples for both modes - live in best_practice.techniques. + (drop route explicit — drop_run). Sub-combat-29 accounts run fast-xp at + the safe South-east Varrock drop site; combat 29+ may use the Al Kharid + 3-rock powermine. Mode NEVER switches to banking. + - banked-income: ONE deterministic target mine_sites row chosen and + validated at session start (Mining level >= ore level; the row's + access/safety gates; inventory policy = bank) and every load is BANKED + until the GP target. Targets: iron @ SE Varrock (safe) and adamantite @ + Al Kharid south (respawn-bound — modeled WITH world hopping per the + wiki money guide's 120+ ores/hr; without hopping the ceiling is ~30 + ores/hr). Runite is NOT selectable: F2P runite sits in the Wilderness + (PK risk, ~12-min respawn) and is out of the modelled set. + Pickaxe policy (two SEPARATE gates, provable): choose the best tier by + MINING level (usability — rune usable at 41 Mining), equip ONLY when the + ATTACK level permits (rune equip = 40 Attack), otherwise CARRY the same + selected tool (a 1-Attack/41-Mining account carries and uses a rune + pickaxe — pinned in pickaxe_examples and recomputed by the validator). accounts: membership: f2p # whole ore chain verified F2P via the wiki guide min_level: 1 # clay/copper/tin mineable at level 1 requires_quest: [] requires_other: - - "Pickaxe: select the best tier by MINING level (usability gate: bronze 1, iron 1, steel 6, mithril 21, adamant 31, rune 41); EQUIP only when the ATTACK requirement is met (equip gate: steel 5, mithril 20, adamant 30, rune 40), otherwise CARRY the same selected tool — mining from the inventory works at every level (a 1-Attack/41-Mining account selects, carries, and uses a rune pickaxe; equipped pickaxe frees one slot, carried costs one — slot counts stay dynamic, never hardcoded)" - - "Run mode chosen at session start: fast-xp (powermine, drop every load) or banked-income (deterministic target ore + mine, bank every load)" + - "Pickaxe: two SEPARATE gates per tier (pickaxe_tiers table): MINING level decides which pickaxe can be USED (bronze 1, iron 1, steel 6, mithril 21, adamant 31, rune 41); ATTACK level decides ONLY whether it can be EQUIPPED (steel 5, mithril 20, adamant 30, rune 40). Select by Mining, equip only when Attack permits, otherwise CARRY the same selected tool — a 1-Attack/41-Mining account selects, carries, and uses a rune pickaxe (pickaxe_examples)" + - "Run mode chosen at session start: fast-xp (powermine, drop every load) or banked-income (deterministic target mine_sites row, bank every load) — mining_modes table" - "Bank route from the chosen mine (fast-xp drops instead when no bank leg is wanted)" - - "Al Kharid powermine HARD GATE: combat level >= 29 (a monster attacks players at <= 2x its level; the scorpion is level 14, so its aggro ceiling is combat 28 and 29+ is safe). Sub-combat-29 accounts MUST use the banked South-east Varrock route (no aggressive NPCs, no food required)" + - "Al Kharid powermine HARD GATE: combat level >= 29 (a monster attacks players at <= 2x its level; the scorpion is level 14, so its aggro ceiling is combat 28 and 29+ is safe). Sub-combat-29 accounts run fast-xp at the South-east Varrock DROP site (no aggressive NPCs) — the mode does not change to banking" + - "adamantite @ Al Kharid south REQUIRES world hopping (mine_sites row: world_hop true, hop_world action) — respawns are 4 min and the wiki's 120+ ores/hr assumes hopping F2P worlds" best_practice: equipment: - - item: "pickaxe: highest tier usable at the account's MINING level (bronze 1, iron 1, steel 6, mithril 21, adamant 31, rune 41)" + - item: "pickaxe: highest tier usable at the account's MINING level (pickaxe_tiers: bronze 1, iron 1, steel 6, mithril 21, adamant 31, rune 41)" why: "each tier is faster — the roll interval drops from 8 ticks (bronze) to 3 ticks (rune), and the guide recommends always using the highest tier available. Two SEPARATE gates: the Mining level decides WHICH pickaxe can be USED, the Attack level decides ONLY whether it can be EQUIPPED (steel 5, mithril 20, adamant 30, rune 40). Equip only when the Attack requirement is met, otherwise carry the same selected tool — a 1-Attack/41-Mining account selects, carries, and uses a rune pickaxe" source: "OSRS wiki — Free-to-play Mining training (Pickaxes: tier table with tick intervals); OSRS wiki — Rune pickaxe (41 Mining to use, 40 Attack to equip) and Steel/Mithril/Adamant pickaxe pages (use vs equip gates)" techniques: - - name: "mode contract: fast-xp vs banked-income (chosen at session start, never silently highest-available)" - why: "fast-xp powermines iron 15+ (copper/tin below 15) and DROPS every full inventory; banked-income fixes ONE target ore + mine, validates it (Mining level, location, inventory policy), logs it in activity state, and BANKS every load. The two modes pick different rocks, use different inventory handling, and stop at different targets — the entry never selects a rock for the operator" - source: "owner decision 2026-08-28 (governor guidance channel, PR #11): expose separate fast-xp and banked-income modes; encoded in this entry's state_machine + a2 params" + - name: "mode contract: fast-xp vs banked-income (chosen at session start, validated against mine_sites, never silently highest-available)" + why: "fast-xp powermines iron 15+ (copper/tin below 15) and DROPS every full inventory; banked-income fixes ONE target mine_sites row (validated: Mining level >= ore level, access/safety gates met, inventory policy bank) and BANKS every load. The two modes pick different rocks, use different inventory handling, and stop at different targets — the entry never selects a rock for the operator" + source: "owner decision 2026-08-28 (governor guidance channel, PR #11): expose separate fast-xp and banked-income modes; encoded in mining_modes + state_machine + a2 params" - name: "start with Doric's Quest for 1,300 xp (level 1 to 10)" why: "the quest reward alone takes Mining from 1 to 10; the materials (6 clay, 4 copper, 2 iron) mine at level 1 except iron, which can be bought on the GE" source: "OSRS wiki — Free-to-play Mining training (Level 1-10: Doric's Quest)" - - name: "powermine iron at Al Kharid mine for maximum XP (fast-xp only, combat 29+ gate)" - why: "iron (level 15, 35 xp) is the fastest F2P training ore and Al Kharid is the only F2P site with three iron rocks around one tile — zero travel between respawns; modeled fast-xp rate ~32,400 XP/hr at 61+ with rune (midpoint policy model, see economy comments); drop the ores, banking costs XP. Entry gate: the mine's level-14 scorpions are aggressive to combat 28, so this spot runs at combat 29+" - source: "OSRS wiki — Free-to-play Mining training (Levels 15-99: Iron ore); OSRS wiki — Aggressiveness" - - name: "bank iron at the South-east Varrock mine (sub-29 default and banked-income anchor)" - why: "the guide names this F2P banking location explicitly; the eastern Varrock bank is the closest bank leg, and the ores feed Smithing. Also the sub-combat-29 route (no aggressive NPCs) and the banked-income anchor: modeled ~56,500 gp/hr with rune (midpoint policy model, see economy comments)" + - name: "powermine iron for maximum XP (fast-xp only)" + why: "iron (level 15, 35 xp) is the fastest F2P training ore; Al Kharid is the only F2P site with three iron rocks around one tile (combat 29+ gate) and South-east Varrock's two iron rocks serve sub-29 accounts with the same drop behaviour — the mode is unchanged. Modeled fast-xp rate ~28,000 XP/hr at 61+ with rune (success model + drop loop + breaks pinned in economy); drop the ores, banking costs XP" + source: "OSRS wiki — Free-to-play Mining training (Levels 15-99: Iron ore); OSRS wiki — Aggressiveness; OSRS wiki — Iron rocks (respawn ~5.4 s)" + - name: "bank iron at the South-east Varrock mine (banked-income anchor)" + why: "the guide names this F2P banking location explicitly; the eastern Varrock bank is the closest bank leg (~30 s per load), and the ores feed Smithing. Modeled ~49,900 gp/hr with rune (midpoint policy model, see economy comments)" source: "OSRS wiki — Free-to-play Mining training (Levels 1/10-15 and 15-99: banking locations)" - - name: "banked-income example: adamantite at the Al Kharid mine (70 Mining)" - why: "deterministic target ore + location: adamantite (70 Mining, 95 xp, 584 gp low edge) at the Al Kharid south rocks, banked at the adjacent bank; modeled ~490,000 gp/hr with rune (midpoint policy model, see economy comments); the wiki cites adamantite banking as a good F2P money method" - source: "OSRS wiki — Free-to-play Mining training (Levels 30-99 and money-making); prices.runescape.wiki snapshot 2026-08-27" + - name: "banked-income example: adamantite at Al Kharid south with world hopping (70 Mining)" + why: "deterministic target row: adamantite (70 Mining, 95 xp, 584 gp low edge) at the Al Kharid south rocks, banked at the adjacent bank. The site is RESPAWN-BOUND (2 rocks, ~4-min respawn): the wiki F2P adamantite money guide reports about 120+ ores/hour ONLY by hopping worlds — modeled 70,080 gp/hr with the hop_world policy; without hopping the ceiling is ~30 ores/hr = 17,520 gp/hr. The entry models the hop, never the idealized iron cadence for a high-tier ore" + source: "OSRS wiki — Money making guide/Mining adamantite ore (F2P) (4-min respawn; ~120 ores/hr with world hopping); OSRS wiki — Free-to-play Mining training (Levels 30-99 and money-making); prices.runescape.wiki snapshot 2026-08-27" + - name: "runite is NOT selectable in this entry" + why: "F2P runite sits in the Wilderness with a ~12-minute respawn — modelling it would require Wilderness travel and PK survival plus heavy world hopping; the mine_sites row is selectable: false and the entry refuses it rather than hand-waving the hazard" + source: "OSRS wiki — Runite ore (respawn ~12 min; F2P rocks in the Wilderness); mine_sites row runite-wilderness" - name: "superheat coal (optional banked variant): West Lumbridge Swamp mine" why: "coal (level 30, 50 xp) banks reasonably from the Lumbridge Swamp mine; with 43 Magic, Superheat Item converts coal into bars without a furnace, cutting bank trips (staff of fire + nature runes)" source: "OSRS wiki — Free-to-play Mining training (Levels 30-99: Superheat Item)" - name: "worked example — fast-xp session (preparation -> first full inventory -> first break -> resume)" - why: "Account: Mining 41, Attack 1, combat 35 (passes the Al Kharid gate). (1) prepare: mode fast-xp; ore policy iron 15+; pickaxe by MINING level -> rune (41 <= 41); equip gate Attack 1 < 40 -> CARRY the same tool (27 ore slots); the rune pickaxe is already owned, so acquire = withdraw from bank; initial inventory = pickaxe + empty ore space. (2) travel: walk to Al Kharid (combat 35 >= 29 OK) and position at the 3-rock cluster. (3) mining rotations: click a fresh iron rock (a2, rune delay [1800, 3600] ms) -> wait_rock: ore gained after the 1.8 s roll -> mine; repeat until the inventory is full. (4) first full-inventory boundary: 27 ores -> drop_run: 27 executions of a6 (right_click Drop, [200, 800] ms each) -> count_item ore == 0 -> mine. (5) first scheduled break: after the 8th-14th rotation the break counter trips -> break_state: take_break 60-180 s (a10). (6) resume: reposition at the cluster (a11) -> mine; the loop repeats until the XP target or session cap -> complete" - source: "derived from this entry's policy model (state_machine prepare/travel/mine/wait_rock/drop_run/break_state/complete + actions a1/a2/a6/a8-a11 + run_control.break), synthesized 2026-08-28" - - name: "worked example — banked-income session (preparation -> first full inventory -> first break -> resume)" - why: "Account: Mining 70, Attack 40, combat 60. Target: 500,000 gp banked or a 2 h session cap. (1) prepare: mode banked-income; target ore = adamantite (ore level 70 <= Mining 70 OK; location = Al Kharid south rocks, which contain adamantite; inventory policy = banked, deposit every load) — target logged in activity state; pickaxe by MINING level -> rune (41 <= 70); equip gate Attack 40 >= 40 -> EQUIP (28 ore slots); tool owned, withdraw from bank. (2) travel: walk to the Al Kharid south adamantite spot. (3) mining rotations: click a fresh adamantite rock (rune roll 1.8 s, delay [1800, 3600] ms) -> wait_rock: ore gained -> mine; repeat. (4) first full-inventory boundary: 28 ores -> bank_run: walk to the adjacent Al Kharid bank (a3), deposit all 28 (a4; load value 28 x 584 = 16,352 gp logged to activity state), walk back (a5) -> mine. (5) first scheduled break: after 8-14 rotations -> break_state: take_break 60-180 s (independent of the bank trips). (6) resume: reposition at the spot (a11) -> mine; complete when the banked value >= 500,000 gp or the session cap hits" - source: "derived from this entry's policy model (state_machine prepare/travel/mine/wait_rock/bank_run/break_state/complete + actions a1-a5/a8-a11 + run_control.break), synthesized 2026-08-28" + why: "Account: Mining 41, Attack 1, combat 35 (passes the Al Kharid gate). (1) prepare: mode fast-xp; ore policy iron 15+; pickaxe by MINING level -> rune (41 <= 41); equip gate Attack 1 < 40 -> CARRY the same tool (27 ore slots); the rune pickaxe is owned, so acquire = a9 withdraw-tool (bank_inventory); initial inventory = pickaxe + empty ore space. (2) travel: walk to Al Kharid (combat 35 >= 29 OK) and position at the 3-rock cluster. (3) mining rotations: click a fresh iron rock (a2, rune cadence [1800, 3600] ms) -> wait_rock: ore gained after ~1.33 rolls (success model) -> mine; repeat until the inventory is full. (4) first full-inventory boundary: 27 ores -> drop_run: 27 executions of a6 (right_click Drop, [200, 800] ms each) -> count_item ore == 0 -> mine. (5) first scheduled break: after the 8th-14th rotation the break counter trips -> break_state: take_break 60-180 s (a15). (6) resume: reposition at the cluster (a16) -> mine; the loop repeats until the XP target or session cap -> complete. Modeled policy rate: 803 ores/hr = 28,105 XP/hr -> pin 28,000 (economy)" + source: "derived from this entry's policy model (state_machine prepare/travel/mine/wait_rock/drop_run/break_state/complete + actions + run_control.break + mine_sites/rolls_per_ore), synthesized 2026-08-28" + - name: "worked example — banked-income adamantite session (preparation -> first full inventory -> first break -> resume)" + why: "Account: Mining 70, Attack 40, combat 60. Target: 500,000 gp banked or a 2 h session cap. (1) prepare: mode banked-income; target row = al-kharid-south-adamantite (mine_sites: ore level 70 <= Mining 70 OK; access/route validated; inventory policy bank) — the row id is logged to activity state; pickaxe by MINING level -> rune (41 <= 70); equip gate Attack 40 >= 40 -> EQUIP (28 ore slots); tool owned, withdraw from bank (a9). (2) travel: walk to the Al Kharid south adamantite spot. (3) mining rotations: click a fresh adamantite rock (rune roll 1.8 s, delay [1800, 3600] ms) -> wait_rock: ore gained -> mine; when BOTH rocks are depleted -> hop: hop_world (a14) to a fresh F2P world (world_hop row) and continue — this is where the wiki's 120 ores/hr comes from. (4) first full-inventory boundary: 28 ores -> bank_run: walk to the Al Kharid bank (a3), deposit all 28 (a4; 28 x 584 = 16,352 gp logged), walk back (a5) -> mine. (5) first scheduled break: after 8-14 rotations -> break_state: take_break 60-180 s (independent of the bank trips). (6) resume: reposition (a16) -> mine; complete when the banked value >= 500,000 gp or the session cap hits" + source: "derived from this entry's policy model (state_machine prepare/travel/mine/wait_rock/hop/bank_run/break_state/complete + actions + mine_sites.al-kharid-south-adamantite), synthesized 2026-08-28" + +pickaxe_tiers: + bronze-pickaxe: + mining_level: 1 # usability gate: Mining level needed to USE this pickaxe + attack_level: 1 # equip gate: Attack level needed only to EQUIP it + roll_ticks: 8 # ore roll interval (ticks x 0.6 s = 4.8 s) + iron-pickaxe: + mining_level: 1 + attack_level: 1 + roll_ticks: 7 + steel-pickaxe: + mining_level: 6 + attack_level: 5 + roll_ticks: 6 + mithril-pickaxe: + mining_level: 21 + attack_level: 20 + roll_ticks: 5 + adamant-pickaxe: + mining_level: 31 + attack_level: 30 + roll_ticks: 4 + rune-pickaxe: + mining_level: 41 + attack_level: 40 + roll_ticks: 3 + +# Structured proof of the two-gate rule (validator recomputes each row from +# pickaxe_tiers and compares): select by MINING, equip only when ATTACK +# permits, carry otherwise. The 1-Attack/41-Mining account is owner-mandated. +pickaxe_examples: + - account: "1-Attack/41-Mining" + attack: 1 + mining: 41 + expected_tier: rune-pickaxe + expected_equipped: false # 41 Mining selects rune; Attack 1 < 40 -> CARRY + - account: "40-Attack/70-Mining" + attack: 40 + mining: 70 + expected_tier: rune-pickaxe + expected_equipped: true # 70 Mining selects rune; Attack 40 >= 40 -> EQUIP + - account: "5-Attack/30-Mining" + attack: 5 + mining: 30 + expected_tier: mithril-pickaxe + expected_equipped: false # 30 Mining selects mithril (21); Attack 5 < 20 -> CARRY + +# Mode contract (owner decision 2026-08-28): both modes explicit; each mode's +# sites/targets reference mine_sites rows; inventory policies are fixed. +mining_modes: + fast-xp: + inventory_policy: drop # powermine: every load dropped, never banked + ore_policy: "iron (level 15, 35 xp) from 15 onward; copper/tin (17.5 xp) below 15 — never highest-available; sub-combat-29 accounts DROP at the South-east Varrock site (safe), combat 29+ may use the Al Kharid powermine — the mode never switches to banking" + sites: [al-kharid-powermine, se-varrock-drop] + banked-income: + inventory_policy: bank # every load banked until the GP target + targets: [se-varrock-bank, al-kharid-south-adamantite] + stop: "banked GP target reached (e.g. 500,000 gp) or session cap" + +# Structured per-site model (owner guidance 2026-08-28): every target row +# carries ore, gates, rock count, respawn, mining-success input, modelled +# rate, hop policy, inventory policy, access/safety and route — the selected +# target in activity state references one row id, so "location contains ore" +# and profitability are validated data, not prose. +mine_sites: + al-kharid-powermine: + ore: Iron ore + ore_id: 440 + mining_level: 15 + xp: 35 + rock_count: 3 # the only F2P site with 3 iron rocks around one tile + respawn_seconds: 5.4 # wiki Iron rocks respawn + rolls_per_ore: 1.33 # success model, 61+ (derived from the wiki 52,500 bench: 1,500 ores/hr / 2,000 3-tick rolls/hr); 41-60 = 1.40 + modeled_ores_per_hour: 803 # fast-xp drop model (economy derivation) + world_hop: false + selectable: true + inventory_policy: drop + access: "combat level >= 29 — the level-14 scorpion's aggro ceiling is combat 28 (wiki Aggressiveness)" + safety: "hard gate + survive-state retreat fallback; no food modeled" + route: "drop in place — no bank leg" + se-varrock-drop: + ore: Iron ore + ore_id: 440 + mining_level: 15 + xp: 35 + rock_count: 2 + respawn_seconds: 5.4 + rolls_per_ore: 1.33 + modeled_ores_per_hour: 803 # same drop model; 2 rocks x 3.59 s mining = 7.2 s > 5.4 s respawn -> no idle + world_hop: false + selectable: true + inventory_policy: drop + access: "no aggressive NPCs — safe at any combat level (the sub-29 fast-xp site)" + safety: "none required" + route: "drop in place — no bank leg" + se-varrock-bank: + ore: Iron ore + ore_id: 440 + mining_level: 15 + xp: 35 + rock_count: 2 + respawn_seconds: 5.4 + rolls_per_ore: 1.33 + modeled_ores_per_hour: 713 # bank model: 3.59 + 0 + 1.07 + 0.39 = 5.05 s/ore (economy derivation) + world_hop: false + selectable: true + inventory_policy: bank + access: "no aggressive NPCs" + safety: "none required" + route: "east Varrock bank leg ~30 s per 28-ore load" + al-kharid-south-adamantite: + ore: Adamantite ore + ore_id: 449 + mining_level: 70 + xp: 95 + rock_count: 2 + respawn_seconds: 240 # wiki F2P adamantite guide: ~4-min respawn + rolls_per_ore: 2.0 # conservative success input at 70 Mining (rate is respawn-bound, not success-bound) + modeled_ores_per_hour: 120 # Sourced measured baseline: wiki money guide ~120 ores/hr WITH world hopping; no-hop ceiling ~30 ores/hr + world_hop: true # hop_world required (a14); the modelled rate assumes it + selectable: true + inventory_policy: bank + access: "Al Kharid south rocks (no aggressive NPCs at this cluster)" + safety: "banked at the Al Kharid bank after every load" + route: "bank leg ~25 s; world-hop (hop_world) when both rocks are depleted — fresh worlds have fresh rocks" + runite-wilderness: + ore: Runite ore + ore_id: 451 + mining_level: 85 + xp: 125 + rock_count: 2 + respawn_seconds: 720 # wiki Runite ore: ~12-min respawn + rolls_per_ore: 3.0 + modeled_ores_per_hour: 0 # not modelled + world_hop: false + selectable: false # F2P runite is in the Wilderness (PK risk) — NOT a target + inventory_policy: bank + access: "Wilderness (PK risk) — NOT modeled" + safety: "out of scope: would require Wilderness travel + PK survival + heavy world hopping" + route: "not selectable in this entry; the hazard is refused, not hand-waved" economy: # Anchor: iron ore (the fast-xp training ore). Sell prices are the # 2026-08-27 snapshot (prices.runescape.wiki latest, low edge). # # MODELLED POLICY RATES (midpoint model, derived 2026-08-28) — the - # entry's OWN parameters, not the wiki's zero-overhead ideals: - # per-ore cycle = a2 anti_ban.delay midpoint (rune [1800, 3600] ms -> - # 2.7 s; the governor ceiling check 3600/2.7 x 35 = 46,667 XP/hr is an - # unreachable ideal even before overhead) + 10% respawn/depletion idle - # (wait_rock absorbs the wiki 3-12 s rock respawn) + drop-or-bank leg - # amortised per ore + break amortised per ore (120 s per 11 rotations, - # run_control.break midpoints). - # fast-xp iron, drop, rune 61+ (equipped): 2.7 + 0.3 + 0.5 (28 drops x - # 0.5 s per load / 28 ores) + 0.39 (120/11/28) = 3.89 s/ore -> ~925 - # ores/hr -> ~32,400 XP/hr (xp_per_hour below). - # banked-income iron, SE Varrock: 2.7 + 0.3 + 1.07 (bank leg ~30 s per - # 28-ore load) + 0.39 = 4.46 s/ore -> ~807 ores/hr -> ~56,500 gp/hr - # (gp_per_hour_high below). - # banked-income adamantite, Al Kharid south: 2.7 + 0.3 + 0.9 (short - # bank leg ~25 s per load) + 0.39 = 4.29 s/ore -> ~839 ores/hr -> - # ~490,000 gp/hr (profit_drivers). The 2-rock spot respawns tighter - # than the 3-rock spot, so treat modeled rates as upper bounds. + # entry's OWN parameters per site (mine_sites), not the wiki's + # zero-overhead ideals: + # success model: a roll is NOT one ore. rolls_per_ore is derived from + # the wiki benchmark for iron at 61+ (52,500 xp/hr = 1,500 ores/hr at a + # perfect 3-tick 2,000 rolls/hr -> 1.33 rolls/ore ~ 75% success; 41-60: + # 50,000 -> 1.40). Lower tiers conservatively more rolls; the + # high-tier sites are respawn-bound, not success-bound. + # per-ore time = rolls_per_ore x 2.7 s (a2 delay midpoint [1800, 3600]) + # + respawn idle + drop-or-bank + break amortised. + # Respawn idle (pinned, NOT a flat 0.3 s): 3 rocks x 3.59 s mining = + # 10.8 s > 5.4 s iron respawn -> ZERO idle (cluster self-sustains); + # 2 rocks x 3.59 s = 7.2 s > 5.4 s -> ZERO idle. Adamantite is the + # opposite: 2 rocks x 240 s respawn -> respawn-BOUND, modelled with + # world hopping (120 ores/hr sourced) — 30 ores/hr without hopping. + # fast-xp iron (drop): 3.59 + 0 + 0.5 (28 x 0.5 s drops / 28) + 0.39 + # (120 s break per 11 rotations / 28) = 4.48 s/ore -> 803 ores/hr -> + # 28,105 XP/hr -> pin 28,000 (xp_per_hour below). + # banked iron @ SE Varrock: 3.59 + 0 + 1.07 (30 s bank leg / 28) + 0.39 + # = 5.05 s/ore -> 713 ores/hr -> 49,910 gp/hr -> pin 49,900. + # banked adamantite @ Al Kharid south (hop): 120 ores/hr (sourced + # measured baseline) x 584 = 70,080 gp/hr; no-hop ceiling ~30 ores/hr + # = 17,520 gp/hr — the entry models ONLY the hop rate. # bronze path (below 15, copper/tin 17.5 xp): cadence midpoint 5.7 s -> - # ~8,800 XP/hr. + # ~8,800 XP/hr. # EXTERNAL BENCHMARK (NOT the modeled rate): the wiki's 50,000 XP/hr # (41+) / 52,500 XP/hr (61+) iron powermine figures assume a continuous # 3-tick rotation with zero delay/drop/break overhead; the configured # midpoint cadence alone caps that ideal at ~46,667 XP/hr, so 52,500 is # cited ONLY as a labeled wiki benchmark and never as this entry's # expected output. - item: Iron ore # fast-xp anchor; banked-income targets in profit_drivers + item: Iron ore # fast-xp anchor; banked-income targets in mine_sites item_id: 440 cost_per_cycle: 0 # mining consumes nothing (pickaxe has no fuel; no supply costs) income_per_cycle: 70 # iron ore sell price (low edge), snapshot 2026-08-27 net_per_cycle: 70 # no per-cycle costs; fast-xp drops ores -> realized income 0 gp_per_hour_low: 0 # fast-xp mode drops every load — XP is the product - gp_per_hour_high: 56500 # MODELLED banked-income rate (iron @ SE Varrock, midpoint model above) - xp_per_hour: 32400 # MODELLED fast-xp policy rate (rune 61+ drop, midpoint model above) — NOT the 52,500 wiki benchmark + gp_per_hour_high: 70080 # top modeled banked-income rate: adamantite WITH world hopping (120 ores/hr x 584); iron @ SE Varrock 49,900; no-hop adamantite 17,520 documented in profit_drivers + xp_per_hour: 28000 # MODELLED fast-xp policy rate (rune 61+ drop, success model + drop loop + breaks) — NOT the 52,500 wiki benchmark supplies: - - item: "pickaxe — best tier selectable by MINING level (bronze 1, iron 1, steel 6, mithril 21, adamant 31, rune 41); equip ONLY when the ATTACK level permits (steel 5, mithril 20, adamant 30, rune 40), otherwise carry the same selected tool (a 1-Attack/41-Mining account carries and uses a rune pickaxe)" + - item: "pickaxe — best tier selectable by MINING level (pickaxe_tiers: bronze 1, iron 1, steel 6, mithril 21, adamant 31, rune 41); equip ONLY when the ATTACK level permits (steel 5, mithril 20, adamant 30, rune 40), otherwise carry the same selected tool (a 1-Attack/41-Mining account carries and uses a rune pickaxe)" qty_per_cycle: 1 source: bank profit_drivers: - "F2P ore chain (wiki guide): clay 1/5xp, copper 1/17.5, tin 1/17.5, iron 15/35, silver 20/40, coal 30/50, gold 40/65, mithril 55/80, adamantite 70/95, runite 85/125" - "Sell prices 2026-08-27 (low edge): iron 70, coal 151, silver 73, gold 154, mithril 159, adamantite 584, runite 10,265" - - "fast-xp: powermine iron 15+ and drop every load — XP is the product, realized gp 0" - - "banked-income (midpoint model, derived 2026-08-28): iron @ SE Varrock ~56,500 gp/hr; adamantite @ Al Kharid south ~490,000 gp/hr — deterministic target ore + location, never highest-available" - - "Money routes (wiki): adamantite banking is a good F2P money method (70 Mining); runite (85 Mining) is one of the fastest F2P money-making methods" + - "fast-xp: powermine iron 15+ and drop every load — XP is the product, realized gp 0; modeled 803 ores/hr -> 28,105 XP/hr (pin 28,000) at 61+ rune" + - "banked-income iron @ SE Varrock: modeled 713 ores/hr -> 49,910 gp/hr (pin 49,900); deterministic target row (mine_sites.se-varrock-bank)" + - "banked-income adamantite @ Al Kharid south: respawn-BOUND site (2 rocks, 4-min respawn) — modeled 120 ores/hr ONLY with world hopping (hop_world) = 70,080 gp/hr; without hopping the ceiling is ~30 ores/hr = 17,520 gp/hr (wiki F2P adamantite money guide)" + - "runite is NOT a selectable target in this entry — F2P runite sits in the Wilderness (PK risk, ~12-min respawn); the mine_sites row is selectable: false" state_machine: initial: prepare terminal: [complete, failed] states: prepare: - goal: > - Session start. 1) Choose the run mode (session param): fast-xp - (powermine, drop) or banked-income (deterministic target ore + - location, bank). 2) fast-xp ore policy: iron 15+ (copper/tin below - 15). banked-income: pick ONE target ore and ONE mine now and - validate: level_is Mining >= ore level, the location contains that - ore, and the inventory policy can hold it (banked -> deposit every - load). 3) Pickaxe: select the highest tier whose MINING - requirement the account meets (bronze 1, iron 1, steel 6, mithril - 21, adamant 31, rune 41 — rune is usable at 41 Mining and - equipable at 40 Attack; a 1-Attack/41-Mining account selects and - CARRIES a rune pickaxe). 4) Equip ONLY when level_is Attack >= - the tier's equip requirement (steel 5, mithril 20, adamant 30, - rune 40); otherwise carry the same selected tool. 5) Acquire the - tool if missing (a8) and build the initial inventory: pickaxe - (equipped or carried) + empty ore space; no food (both routes are - gate-safe). 6) Record mode + target in activity state — the target - must be visible, never implicit. + goal: >- + Session start. 1) Choose the run mode (mining_modes): fast-xp + (powermine, drop) or banked-income (deterministic target mine_sites + row, bank). 2) fast-xp ore policy: iron 15+ (copper/tin below 15), + site from mining_modes.fast-xp.sites (combat 29+ -> Al Kharid + powermine, else SE Varrock drop — SAME mode, never banking). + banked-income: pick ONE row from mining_modes.banked-income.targets + and validate: level_is Mining >= row mining_level, the row's access + gates hold (e.g. adamantite requires the hop policy), inventory + policy == bank; the row id is logged to activity state. 3) Pickaxe: + select the highest pickaxe_tiers row whose mining_level the account + meets (rune is usable at 41 Mining and equipable at 40 Attack). + 4) Equip ONLY when level_is Attack >= the row's attack_level; + otherwise carry the same selected tool (a 1-Attack/41-Mining account + selects and CARRIES a rune pickaxe). 5) Acquire the tool if missing + (a8-a12: check / withdraw-owned / buy / wait / collect) and build + the initial inventory: pickaxe (equipped or carried) + empty ore + space; no food (all routes gate-safe). 6) Record mode + target row + id in activity state. next: [travel, complete, failed] travel: - goal: "walk/teleport to the selected mine (fast-xp: Al Kharid 3-rock powermine at combat 29+, else banked South-east Varrock; banked-income: the session's validated target mine) and position at the target cluster; arrive with the pickaxe equipped or carried per the prepare gate" + goal: "walk/teleport to the selected mine_sites location (fast-xp: Al Kharid 3-rock powermine at combat 29+, else South-east Varrock; banked-income: the session's validated target row) and position at the target cluster; arrive with the pickaxe equipped or carried per the prepare gate" next: [mine, recover] mine: - goal: > - Mine the session target rock until the inventory is full or the - run target is reached. Rock selection is MODE-DRIVEN and - deterministic: fast-xp mines iron (15+) or copper/tin (<15); - banked-income mines ONLY the session target ore (never the highest - available rock). Every a2 click is followed by the wait_rock - outcome wait — never chain clicks on the same rock. - next: [wait_rock, drop_run, bank_run, recover, survive, complete] + goal: >- + Mine the session target rock until the inventory is full or the run + target is reached. Rock selection is MODE-DRIVEN and deterministic: + fast-xp mines iron (15+) or copper/tin (<15); banked-income mines + ONLY the session's target mine_sites row (never the highest + available rock). Every a2 click is followed by the wait_rock outcome + wait — never chain clicks on the same rock. A world_hop site + (adamantite) hops when both rocks are depleted. + next: [wait_rock, drop_run, bank_run, hop, recover, survive, complete] wait_rock: - goal: "after clicking a rock (a2), wait for the mining outcome — ore gained, rock depleted, or roll timeout (2x the equipped tier's roll) — before any re-click; never re-click the active rock while its roll is unresolved; a fully depleted cluster is a respawn wait, absorbed here (wiki rock respawn 3-12 s)" - next: [mine, drop_run, bank_run, recover] + goal: "after clicking a rock (a2), wait for the mining outcome — ore gained (may take several rolls: rolls_per_ore), rock depleted, or roll timeout (2x the equipped tier's roll) — before any re-click; never re-click the active rock while its roll is unresolved; respawns of the site's rock_count rocks are absorbed here (wiki respawn figures in mine_sites)" + next: [mine, hop, drop_run, bank_run, recover] + hop: + goal: "world_hop sites ONLY (al-kharid-south-adamantite): both target rocks are depleted -> hop_world (a14) to a fresh F2P world and continue mining its fresh rocks; hop count tracked; after 8 consecutive hops with no ore gained, go to recover (possible world lockout / price-stale)" + next: [mine, recover] drop_run: goal: "fast-xp ONLY: drop every occupied ore slot — one right-click Drop per slot (a6 re-executed), looping until count_item ore == 0. Slot count is DYNAMIC: inventory capacity minus non-ore items (equipped pickaxe = 28 ore slots, carried = 27, any session load = fewer) — never a hardcoded loop count" next: [drop_run, mine, break_state, complete] bank_run: - goal: "banked-income ONLY: walk to the nearest bank, deposit all ores (activity state: target ore + load value logged), walk back to the mine; the pickaxe stays (equipped or carried) and the inventory is rebuilt as empty ore space for the next load" + goal: "banked-income ONLY: walk to the nearest bank, deposit all ores (activity state: target row id + load value logged), walk back to the mine; the pickaxe stays (equipped or carried) and the inventory is rebuilt as empty ore space for the next load" next: [mine, break_state, complete] break_state: - goal: "scheduled break independent of incidental banking: after every run_control.break.every rotations (8-14) take_break (60-180 s AFK, a10), then reposition at the cluster (a11) and resume mining" + goal: "scheduled break independent of incidental banking: after every run_control.break.every rotations (8-14) take_break (60-180 s AFK, a15), then reposition at the cluster (a16) and resume mining" next: [mine, complete] recover: - goal: "diagnose and fix: no fresh rock of the target tier for 2 respawn cycles (rotate cluster / wait respawn); invalid location (is_at fails — wrong mine or wrong ore) -> re-travel to the correct mine; missing tool (has_item pickaxe false) -> acquire (a8) and re-prepare; path failure (move_to on_fail) -> retry travel. Persistent after 3 recoveries -> failed" + goal: "diagnose and fix: no fresh rock of the target tier for 2 respawn cycles (rotate cluster / wait respawn / hop for world_hop rows); invalid location (is_at fails — wrong mine or wrong ore) -> re-travel to the correct mine_sites location; missing tool (has_item pickaxe false) -> acquire (a8-a12) and re-prepare; path failure (move_to on_fail) -> retry travel. Persistent after 3 recoveries -> failed" next: [prepare, travel, mine, failed] survive: goal: "defensive fallback only (the 29+ gate means scorpions never aggro at Al Kharid): if attacked anyway (gate bypassed/griefed), retreat out of the aggro radius and re-enter the cluster when safe; no food loop is modeled" @@ -182,16 +334,16 @@ state_machine: goal: "hard failure — stop, log ERROR sentinel, alert" transitions: - from: prepare - when: "mode + target set and validated, tool ready, inventory built" + when: "mode + target row set and validated (mine_sites), tool ready, inventory built" to: travel - from: prepare when: "target already reached at session start (fast-xp level target / banked GP target)" to: complete - from: prepare - when: "3 consecutive failed tool acquisitions or an unvalidatable target (invalid banked ore/location combination)" + when: "3 consecutive failed tool acquisitions or an unvalidatable target (invalid banked ore/location combination, or tool purchase/equip failure)" to: failed - from: travel - when: "is_at the selected mine and positioned at the cluster" + when: "is_at the selected mine_sites location and positioned at the cluster" to: mine - from: travel when: "path failure (move_to retries exhausted)" @@ -205,6 +357,9 @@ state_machine: - from: mine when: "inventory full (banked-income mode)" to: bank_run + - from: mine + when: "both target rocks depleted AND the site's world_hop == true" + to: hop - from: mine when: "no fresh target-tier rock available for 2 respawn cycles, or has_item pickaxe == false (tool lost/griefed)" to: recover @@ -220,6 +375,9 @@ state_machine: - from: wait_rock when: "roll timeout (no outcome within 2x the equipped tier's roll interval)" to: mine + - from: wait_rock + when: "both target rocks depleted AND the site's world_hop == true" + to: hop - from: wait_rock when: "inventory full (fast-xp mode)" to: drop_run @@ -229,6 +387,12 @@ state_machine: - from: wait_rock when: "cluster fully depleted and the respawn wait exceeded (no rock of the target tier in range)" to: recover + - from: hop + when: "landed on a fresh F2P world with target rocks available" + to: mine + - from: hop + when: "8 consecutive hops with no ore gained (possible world lockout)" + to: recover - from: drop_run when: "inventory still holds ore (count_item ore > 0)" to: drop_run @@ -257,13 +421,13 @@ state_machine: when: "session cap reached during the break" to: complete - from: recover - when: "tool missing -> re-acquired (a8) or withdrawn from bank" + when: "tool missing -> re-acquired (a8-a12) or withdrawn from bank" to: prepare - from: recover - when: "wrong location -> re-routed to the correct mine" + when: "wrong location -> re-routed to the correct mine_sites location" to: travel - from: recover - when: "rocks unavailable -> respawned or rotated to a fresh cluster of the target tier" + when: "rocks unavailable -> respawned or rotated to a fresh cluster of the target tier (or hopped, for world_hop rows)" to: mine - from: recover when: "recovery looped 3 times (persistent tool/location/path failure)" @@ -279,12 +443,12 @@ actions: - id: a1 step: travel-to-mine verb: move_to - target: "the mode's mine" + target: "the mode's mine_sites location" params: - location: "fast-xp: Al Kharid Mine powermine (combat 29+ only) or South-east Varrock mine (sub-29 default); banked-income: the session's validated target mine (example: Al Kharid south adamantite rocks)" + location: "fast-xp: Al Kharid Mine powermine (combat 29+ only) or South-east Varrock mine (sub-29 default — SAME drop mode); banked-income: the session's validated target row (example: Al Kharid south adamantite rocks)" method: [walk, teleport] - note: "arrive with the pickaxe equipped ONLY if level_is Attack >= the tier's equip requirement (rune = 40 Attack); otherwise carry the same selected tool in the inventory" - route_gate: "Al Kharid powermine ONLY at combat level >= 29 (level_is Combat >= 29 — the level-14 scorpion's aggro ceiling is combat 28, wiki Aggressiveness); below combat 29 use the banked South-east Varrock route (no aggressive NPCs, no food required); the Al Kharid south adamantite spot sits outside the scorpion cluster and is gate-checked the same way" + note: "arrive with the pickaxe equipped ONLY if level_is Attack >= the tier's attack_level (rune = 40 Attack); otherwise carry the same selected tool in the inventory" + route_gate: "Al Kharid powermine ONLY at combat level >= 29 (level_is Combat >= 29 — the level-14 scorpion's aggro ceiling is combat 28, wiki Aggressiveness); below combat 29 run fast-xp at the South-east Varrock DROP site (no aggressive NPCs, same drop behaviour — the mode does not change); the Al Kharid south adamantite spot sits outside the scorpion cluster" on_fail: retry anti_ban: delay: [400, 1600] @@ -294,16 +458,16 @@ actions: verb: click_object target: "fresh rock of the session target tier — never the rock whose roll is in progress" params: - rock: "MODE-DRIVEN: fast-xp -> iron rock (level 15+; copper/tin below 15); banked-income -> ONLY the session target ore validated in prepare (silver 20, coal 30, gold 40, mithril 55, adamantite 70, runite 85) — never highest-available" - gate: "level_is Mining >= rock level (e.g. adamantite = 70), and for banked-income the rock must be at the session's validated location" - mode_visible: "activity state logs mode + target ore (id + name) on every rotation — the target is visible, never implicit" - rotation: "3-rock cluster (Al Kharid) or the nearest cluster of the target tier; face the respawning rock and click a fresh rock only after the wait_rock outcome wait" + rock: "MODE-DRIVEN: fast-xp -> iron rock (level 15+; copper/tin below 15); banked-income -> ONLY the session's target mine_sites row (silver 20, coal 30, gold 40, mithril 55, adamantite 70) — never highest-available" + gate: "level_is Mining >= the site row's mining_level (e.g. adamantite = 70), and for banked-income the rock must be at the session's validated mine_sites location" + mode_visible: "activity state logs mode + target row id + ore name on every rotation — the target is visible, never implicit" + rotation: "the site's rock_count cluster (Al Kharid 3-rock / SE Varrock 2-rock / adamantite 2-rock); click a fresh rock only after the wait_rock outcome wait (rolls_per_ore success model)" roll_table: "pickaxe roll interval (ticks x 0.6 s, wiki table verified 2026-08-27): bronze 8 = 4.8 s, iron 7 = 4.2 s, steel 6 = 3.6 s, mithril 5 = 3.0 s, adamant 4 = 2.4 s, rune 3 = 1.8 s" - cadence: "mandatory pause per click = the equipped tier's full roll interval from roll_table + [0, 1800] ms humanisation: rune [1800, 3600], adamant [2400, 4200], mithril [3000, 4800], steel [3600, 5400], iron [4200, 6000], bronze [4800, 6600]. The pause must never be shorter than the equipped tier's FULL roll, and the wait_rock outcome wait must elapse before any re-click — clicking before the roll resolves cannot speed the ore and is the bot tell" + cadence: "mandatory pause per click = the equipped tier's full roll interval from roll_table + [0, 1800] ms humanisation: rune [1800, 3600], adamant [2400, 4200], mithril [3000, 4800], steel [3600, 5400], iron [4200, 6000], bronze [4800, 6600]. The pause must never be shorter than the equipped tier's FULL roll, the wait_rock success model runs rolls_per_ore rolls per ore, and the outcome wait must elapse before any re-click — clicking before the outcome resolves cannot speed the ore and is the bot tell" wait_for: "mining outcome — modeled as the wait_rock state: re-click a fresh rock only when ore was gained, the clicked rock depleted, or the roll timed out (2x roll)" on_fail: retry anti_ban: - delay: [1800, 3600] # rune-pickaxe anchor (61+): full 1.8 s roll + up to 1.8 s humanisation; per-tier floor in params.roll_table/cadence — never below the equipped tier's full roll + delay: [1800, 3600] # rune-pickaxe anchor (61+): full 1.8 s roll + up to 1.8 s humanisation per roll attempt; per-tier floor in roll_table/cadence - id: a3 step: bank-approach @@ -323,7 +487,7 @@ actions: params: deposit: [all ores] withdraw: "nothing (banked-income mode; the pickaxe stays equipped or carried — inventory rebuilt as empty ore space for the next load)" - note: "log the load to activity state: target ore, qty, banked value" + note: "log the load to activity state: target row id, ore, qty, banked value" on_fail: alert anti_ban: delay: [500, 1100] @@ -362,58 +526,110 @@ actions: delay: [600, 1800] - id: a8 - step: acquire-tool + step: check-tool + verb: has_item + target: "the selected pickaxe tier (Mining-gated: pickaxe_tiers)" + params: + branch: "owned -> a9 withdraw-tool (or straight to a13 equip-tool if already in the inventory); missing -> a10 buy-tool (GE purchase path)" + verify: "inventory holds exactly one pickaxe of the selected tier — never a second pickaxe" + on_fail: skip + anti_ban: + delay: [300, 900] + + - id: a9 + step: withdraw-tool + verb: bank_inventory + target: bank + params: + withdraw: "1x selected pickaxe tier (owned-tool branch)" + note: "the owned tool comes from the bank — never bought twice" + on_fail: retry + anti_ban: + delay: [500, 1200] + + - id: a10 + step: buy-tool verb: ge_buy target: grand-exchange params: - item: "the selected pickaxe tier (Mining-gated: bronze 1, iron 1, steel 6, mithril 21, adamant 31, rune 41)" + item: "the selected pickaxe tier (Mining-gated)" qty: 1 max_price: "GE mid + 10% cap" - note: "acquisition route for prepare/recover: when the tool is already owned, withdraw it from the bank instead (bank_inventory withdraw 1x pickaxe); buy only what is missing — never a second pickaxe in the inventory" + pair_with: "a11 wait-ge-fill (offer fill wait) then a12 collect-tool (collect + verify)" on_fail: retry anti_ban: delay: [1200, 3000] - - id: a9 - step: equip-tool - verb: use_item_on - target: "self" + - id: a11 + step: wait-ge-fill + verb: ge_offers_done + target: "the pickaxe purchase offer" params: - item: "the selected pickaxe tier" - on: "self (equip)" - gate: "RUN ONLY IF level_is Attack >= the tier's equip requirement (steel 5, mithril 20, adamant 30, rune 40) — otherwise SKIP and carry the same selected tool in the inventory; mining from the inventory works at any level (a 1-Attack/41-Mining account mines with a carried rune pickaxe)" + until: "the pickaxe offer fills or times out — never proceed with a pending offer" + on_fail: retry + anti_ban: + delay: [900, 2400] + + - id: a12 + step: collect-tool + verb: ge_collect + target: grand-exchange + params: + verify: "has_item pickaxe == true after collection; otherwise recover (a8 path) — a failed purchase or equip is diagnosed, never assumed" + on_fail: retry + anti_ban: + delay: [600, 1400] + + - id: a13 + step: equip-tool + verb: menu_option + target: "the selected pickaxe tier in the inventory" + params: + option: "Wield" + gate: "RUN ONLY IF level_is Attack >= the tier's attack_level (steel 5, mithril 20, adamant 30, rune 40) — otherwise SKIP and carry the same selected tool in the inventory; mining from the inventory works at any level (a 1-Attack/41-Mining account mines with a carried rune pickaxe)" on_fail: skip anti_ban: delay: [600, 1400] - - id: a10 + - id: a14 + step: hop-world + verb: hop_world + target: "a fresh F2P world" + params: + site: "world_hop rows ONLY (al-kharid-south-adamantite) — fired when both target rocks are depleted (hop state); each landed world has fresh rocks" + count: "hop count tracked; after 8 consecutive hops with no ore, go to recover" + on_fail: retry + anti_ban: + delay: [900, 2200] + + - id: a15 step: take-break verb: take_break target: "self" params: duration: "run_control.break.for [60, 180] s of idle AFK" - note: "breaks are independent of incidental banking: the counter runs on rotations (run_control.break.every [8, 14]), not on bank visits" + note: "breaks are independent of incidental banking: the counter runs on rotations (run_control.break.every [8, 14]), not on bank visits or hops" on_fail: retry anti_ban: delay: [300, 900] - - id: a11 + - id: a16 step: reposition-cluster verb: walk_to target: "cluster center / fresh rock of the target tier" params: - location: "the session's mine, cluster of the target tier" + location: "the session's mine_sites location, cluster of the target tier" note: "recovery + post-break reposition: after take_break, respawn waits, or a recover diagnosis, walk to the cluster's centre tile and re-select a fresh rock; never click a rock while walking" on_fail: retry anti_ban: delay: [400, 1200] run_control: - iteration: "one full mine -> drop (fast-xp) or mine -> bank (banked-income) rotation" + iteration: "one full mine -> drop (fast-xp) or mine -> bank (banked-income) rotation (world_hop rows include the hop legs)" max_iterations: null break: - every: [8, 14] # rotations; the break counter is independent of incidental banking - for: [60, 180] # seconds of idle AFK (take_break, a10) + every: [8, 14] # rotations; the break counter is independent of incidental banking and hops + for: [60, 180] # seconds of idle AFK (take_break, a15) stop_conditions: - "fast-xp: Mining XP/level target reached (session target); banked-income: banked GP target reached or session cap" - "3 consecutive failed rock clicks (client issue — do not loop)" @@ -425,12 +641,17 @@ risk: notes: - "Mining is a staple low-risk method: long, repetitive, low-input behaviour that matches normal player sessions — the guide itself recommends hours of drop-mining." - "The powermine rotation is the detection vector if executed perfectly for hours — keep the a2 tier-aware cadence (wait_rock outcome wait + roll_table pause; anchor rune 1.8-3.6 s, never below the equipped tier's full roll) and take the break schedule." - - "Al Kharid mine has aggressive level-14 scorpions (they attack players at combat level <= 28 — wiki Aggressiveness). The entry HARD-gates Al Kharid to combat 29+; sub-29 accounts run the banked South-east Varrock route (no aggressive NPCs). Both modes select the pickaxe by Mining level and equip only when the Attack level permits (rune 41 Mining usable / 40 Attack equip: a 1-Attack/41-Mining account carries and uses a rune pickaxe). No food is part of either route; the survive state (retreat) is a defensive fallback only." + - "Al Kharid mine has aggressive level-14 scorpions (they attack players at combat level <= 28 — wiki Aggressiveness). The entry HARD-gates Al Kharid to combat 29+; sub-29 accounts run fast-xp at the South-east Varrock DROP site (no aggressive NPCs, same mode). No food is part of either route; the survive state (retreat) is a defensive fallback only." + - "World hopping (adamantite row only) is pace-sensitive: hop at the depletion trigger, not on a timer, and back off after 8 fruitless hops. The modelled 120 ores/hr is the wiki's hopping baseline — never claim it without the hop policy." + - "Bottom line on rates: 28,000 XP/hr (fast-xp) and 70,080 gp/hr (adamantite w/ hop) are this entry's modelled policy rates derived in economy; the wiki's 52,500 XP/hr is a labeled external benchmark, not an achieved rate." sources: - "OSRS wiki — Active/passive aggressive monsters: Aggressiveness (a monster attacks players whose combat level is <= 2x its level; a level-14 scorpion is safe from combat level 29) — verified 2026-08-27" - "OSRS wiki — Scorpion (combat level 14, hitpoints 17; Al Kharid mine) — verified 2026-08-27" - - "OSRS wiki — Free-to-play Mining training (ore level/xp table — all rows F2P; pickaxe tier table with tick intervals (bronze 8 -> rune 3); Doric's Quest 1,300 xp; iron 50,000 xp/hr at 41+ / 52,500 at 61+ cited as zero-overhead external benchmarks only; Al Kharid 3-rock spot; SE Varrock banking; Superheat coal; adamantite/runite as F2P money methods; scorpions noted — this entry hard-gates the mine to combat 29+ instead, so food is not modeled) — verified 2026-08-27" + - "OSRS wiki — Free-to-play Mining training (ore level/xp table — all rows F2P; pickaxe tier table with tick intervals (bronze 8 -> rune 3); Doric's Quest 1,300 xp; iron 50,000 xp/hr at 41+ / 52,500 at 61+ cited as zero-overhead external benchmarks only — the configured midpoint cadence caps the ideal at ~46,667; Al Kharid 3-rock spot; SE Varrock banking; Superheat coal; scorpions noted — this entry hard-gates the mine to combat 29+ instead, so food is not modeled) — verified 2026-08-27" + - "OSRS wiki — Money making guide/Mining adamantite ore (F2P) (4-minute respawn; about 120+ ores/hour by hopping worlds only; at the committed 584 gp that is <=17,520 gp/hr without hopping or ~70,080 gp/hr at 120 ores/hr) — verified 2026-08-28" + - "OSRS wiki — Iron rocks (respawn approximately 5.4 seconds) — verified 2026-08-28" + - "OSRS wiki — Runite ore (respawn about twelve minutes; F2P rocks in the Wilderness) — verified 2026-08-28" - "OSRS wiki — Mining (skill overview, members=No)" - "OSRS wiki — Rune pickaxe (41 Mining to use, 40 Attack to equip) / Adamant pickaxe (31 Mining, 30 Attack) / Mithril pickaxe (21 Mining, 20 Attack) / Steel pickaxe (6 Mining, 5 Attack) — use gate vs equip gate per tier — verified 2026-08-28" - "prices.runescape.wiki/api/v1/osrs/mapping (ore ids: iron 440, coal 453, silver 442, gold 444, mithril 447, adamantite 449, runite 451 — all members=false; buy limits)" diff --git a/schemas/entry.schema.json b/schemas/entry.schema.json index c50ce9b..1a5b49f 100644 --- a/schemas/entry.schema.json +++ b/schemas/entry.schema.json @@ -62,6 +62,97 @@ } }, + "pickaxe_tiers": { + "type": "object", + "minProperties": 1, + "propertyNames": { "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$" }, + "additionalProperties": { + "type": "object", + "required": ["mining_level", "attack_level", "roll_ticks"], + "additionalProperties": false, + "properties": { + "mining_level": { "type": "integer", "minimum": 1, "maximum": 126 }, + "attack_level": { "type": "integer", "minimum": 1, "maximum": 126 }, + "roll_ticks": { "type": "integer", "minimum": 1 } + } + } + }, + + "pickaxe_examples": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": ["account", "attack", "mining", "expected_tier", "expected_equipped"], + "additionalProperties": false, + "properties": { + "account": { "type": "string", "minLength": 2 }, + "attack": { "type": "integer", "minimum": 1, "maximum": 126 }, + "mining": { "type": "integer", "minimum": 1, "maximum": 126 }, + "expected_tier": { "type": "string", "minLength": 2 }, + "expected_equipped": { "type": "boolean" } + } + } + }, + + "mining_modes": { + "type": "object", + "required": ["fast-xp", "banked-income"], + "additionalProperties": false, + "properties": { + "fast-xp": { + "type": "object", + "required": ["inventory_policy", "ore_policy", "sites"], + "additionalProperties": false, + "properties": { + "inventory_policy": { "const": "drop" }, + "ore_policy": { "type": "string", "minLength": 5 }, + "sites": { "type": "array", "minItems": 1, "items": { "type": "string" } } + } + }, + "banked-income": { + "type": "object", + "required": ["inventory_policy", "targets", "stop"], + "additionalProperties": false, + "properties": { + "inventory_policy": { "const": "bank" }, + "targets": { "type": "array", "minItems": 1, "items": { "type": "string" } }, + "stop": { "type": "string", "minLength": 5 } + } + } + } + }, + + "mine_sites": { + "type": "object", + "minProperties": 1, + "propertyNames": { "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$" }, + "additionalProperties": { + "type": "object", + "required": ["ore", "ore_id", "mining_level", "xp", "rock_count", + "respawn_seconds", "rolls_per_ore", "modeled_ores_per_hour", + "world_hop", "selectable", "inventory_policy", "access", + "safety", "route"], + "additionalProperties": false, + "properties": { + "ore": { "type": "string", "minLength": 2 }, + "ore_id": { "type": "integer", "minimum": 1 }, + "mining_level": { "type": "integer", "minimum": 1, "maximum": 126 }, + "xp": { "type": "number", "exclusiveMinimum": 0 }, + "rock_count": { "type": "integer", "minimum": 1 }, + "respawn_seconds": { "type": "number", "exclusiveMinimum": 0 }, + "rolls_per_ore": { "type": "number", "exclusiveMinimum": 0 }, + "modeled_ores_per_hour": { "type": "number", "minimum": 0 }, + "world_hop": { "type": "boolean" }, + "selectable": { "type": "boolean" }, + "inventory_policy": { "enum": ["drop", "bank"] }, + "access": { "type": "string", "minLength": 3 }, + "safety": { "type": "string", "minLength": 3 }, + "route": { "type": "string", "minLength": 5 } + } + } + }, + "best_practice": { "type": "object", "required": ["equipment"], diff --git a/tools/validate.py b/tools/validate.py index afcc8e9..c3bacf9 100644 --- a/tools/validate.py +++ b/tools/validate.py @@ -11,15 +11,38 @@ Runs: => WARNING (entry should be re-verified, not dropped) - `best_practice` lines whose source starts with "researched" => WARNING (citation TODO — file a follow-up issue) + - Mining model (pickaxe gates + run-mode contract) — STRUCTURED + checks on the declared `pickaxe_tiers` / `pickaxe_examples` / + `mining_modes` / `mine_sites` blocks (owner guidance 2026-08-28): + * pickaxe tier values must match the canonical F2P table exactly + (Mining = usability gate, Attack = equip gate only, roll ticks); + * every pickaxe_examples row is recomputed (select highest tier + whose mining_level <= mining; equipped = attack >= that tier's + attack_level) and must match the declared expectation — the + pinned 1-Attack/41-Mining account must select and CARRY rune; + * both run modes must be declared with the correct inventory + policy (fast-xp = drop, banked-income = bank) and only + reference declared, selectable sites whose policy matches; + * a selectable banked site with respawn >= 120 s MUST model + world hopping (world_hop: true AND a hop_world action in the + entry) — long-respawn sites are not executable otherwise; + * a selectable site whose access is Wilderness is rejected + (PK hazard cannot be hand-waved); + * modeled_ores_per_hour must not exceed the cadence ceiling + 3600 / (rolls_per_ore x 2.7s rune delay midpoint); + * the entry's economy.xp_per_hour (fast-xp headline) must sit + within 2% of the fastest declared fast-xp site's modelled + ores/hr x its ore xp. 3. Global id uniqueness across the whole codex tree. Exit codes: 0 = clean (warnings ok), 1 = validation errors. -Usage: python3 tools/validate.py [--stale-days 30] +Usage: python3 tools/validate.py [--stale-days 30] [--selftest] + --selftest: run the committed regression fixtures only. """ + import datetime import json -import re import sys from pathlib import Path @@ -31,6 +54,22 @@ SCHEMA = json.loads((ROOT / "schemas" / "entry.schema.json").read_text()) CODEX = ROOT / "codex" STALE_DAYS = 30 +# Canonical F2P pickaxe table (OSRS wiki pickaxe pages, verified 2026-08-28): +# mining_level = which pickaxe can be USED; attack_level = only whether it can +# be EQUIPPED; roll_ticks = ore roll interval (ticks x 0.6 s). +CANONICAL_PICKAXES = { + "bronze-pickaxe": {"mining_level": 1, "attack_level": 1, "roll_ticks": 8}, + "iron-pickaxe": {"mining_level": 1, "attack_level": 1, "roll_ticks": 7}, + "steel-pickaxe": {"mining_level": 6, "attack_level": 5, "roll_ticks": 6}, + "mithril-pickaxe": {"mining_level": 21, "attack_level": 20, "roll_ticks": 5}, + "adamant-pickaxe": {"mining_level": 31, "attack_level": 30, "roll_ticks": 4}, + "rune-pickaxe": {"mining_level": 41, "attack_level": 40, "roll_ticks": 3}, +} +# a2 anti_ban delay midpoint [1800, 3600] ms — the paced roll-attempt cadence +# the rate model derives from (rune anchor; see mining-f2p.yaml economy). +RUNE_DELAY_MID_S = 2.7 +XP_TOLERANCE = 0.02 + def load_entries(): out = [] @@ -41,12 +80,335 @@ def load_entries(): return sorted(out) +def select_pickaxe(mining_level, tiers): + """Highest tier whose mining_level gate the account meets (None if none).""" + best, best_lvl = None, -1 + for tid, tdef in (tiers or {}).items(): + if not isinstance(tdef, dict): + continue + gate = tdef.get("mining_level") + if isinstance(gate, int) and gate <= mining_level and gate > best_lvl: + best, best_lvl = tid, gate + return best + + +def mining_model_errors(rel, data): + """STRUCTURED mining-model gates (owner guidance 2026-08-28; replaces the + earlier phrase-search checks, which could pass on comments alone).""" + errors = [] + tiers = data.get("pickaxe_tiers") + if tiers is None: + return errors # non-mining entry — not gated + if not isinstance(tiers, dict): + return [f"{rel}: 'pickaxe_tiers' must be a mapping of tier id -> gates"] + + # --- 1. canonical tier values (exact) --- + for tid, canon in CANONICAL_PICKAXES.items(): + got = tiers.get(tid) + if not isinstance(got, dict): + errors.append( + f"{rel}: pickaxe tier '{tid}' missing from pickaxe_tiers " + f"(canonical F2P table: mining {canon['mining_level']} use / " + f"attack {canon['attack_level']} equip / {canon['roll_ticks']} ticks)") + continue + for key, want in canon.items(): + if got.get(key) != want: + errors.append( + f"{rel}: pickaxe tier '{tid}' {key}={got.get(key)}; canonical " + f"value is {want} (Mining = usability gate, Attack = equip " + f"gate ONLY, roll_ticks = ore roll interval)") + for tid in tiers: + if tid not in CANONICAL_PICKAXES: + errors.append(f"{rel}: pickaxe_tiers declares unknown tier '{tid}'") + + # --- 2. examples recomputed from the DECLARED tiers (two-gate proof) --- + examples = data.get("pickaxe_examples") + if not isinstance(examples, list) or not examples: + errors.append(f"{rel}: pickaxe_examples must declare at least one account " + f"example for the two-gate selection proof") + else: + has_141 = False + for ex in examples: + if not isinstance(ex, dict): + continue + atk, mng = ex.get("attack"), ex.get("mining") + if atk == 1 and mng == 41: + has_141 = True + chosen = select_pickaxe(mng, tiers) + if chosen is None: + errors.append(f"{rel}: example '{ex.get('account')}' (Mining {mng}) " + f"has no eligible pickaxe tier") + continue + tdef = tiers.get(chosen) or {} + should_equip = atk >= tdef.get("attack_level", 10**9) + if ex.get("expected_tier") != chosen: + errors.append( + f"{rel}: example '{ex.get('account')}' (Attack {atk}/Mining " + f"{mng}) expects tier '{ex.get('expected_tier')}' but the " + f"Mining-use gate selects '{chosen}'") + if ex.get("expected_equipped") != should_equip: + errors.append( + f"{rel}: example '{ex.get('account')}' (Attack {atk}/Mining " + f"{mng}) expects equipped={ex.get('expected_equipped')} but " + f"the Attack-equip gate on '{chosen}' says equipped={should_equip}") + if not has_141: + errors.append( + f"{rel}: pickaxe_examples must include the owner-mandated " + f"1-Attack/41-Mining account (selects, carries, and uses a rune " + f"pickaxe — equipped=false)") + + # --- 3. run-mode contract --- + modes = data.get("mining_modes") + sites = data.get("mine_sites") + if not isinstance(modes, dict) or not isinstance(sites, dict): + errors.append(f"{rel}: a mining entry must declare both 'mining_modes' " + f"and 'mine_sites' blocks") + return errors + if modes.get("fast-xp", {}).get("inventory_policy") != "drop": + errors.append(f"{rel}: mining_modes.fast-xp.inventory_policy must be " + f"'drop' (powermine drops every load — never banks)") + if modes.get("banked-income", {}).get("inventory_policy") != "bank": + errors.append(f"{rel}: mining_modes.banked-income.inventory_policy must " + f"be 'bank'") + + def site_errors(site_id, where, want_policy): + out = [] + sdef = sites.get(site_id) + if not isinstance(sdef, dict): + out.append(f"{rel}: {where} references '{site_id}' which is not " + f"declared in mine_sites") + return out + if sdef.get("selectable") is not True: + out.append(f"{rel}: {where} references '{site_id}' which is marked " + f"selectable: false (not an eligible target)") + if sdef.get("inventory_policy") != want_policy: + out.append(f"{rel}: {where} site '{site_id}' has inventory_policy " + f"'{sdef.get('inventory_policy')}' but the mode requires " + f"'{want_policy}'") + if sdef.get("selectable") is True and any( + not str(sdef.get(k, "")).strip() for k in ("access", "safety", "route")): + out.append(f"{rel}: selectable site '{site_id}' must carry access, " + f"safety and route descriptions") + return out + + for sid in modes.get("fast-xp", {}).get("sites", []) or []: + errors += site_errors(sid, "mining_modes.fast-xp.sites", "drop") + for sid in modes.get("banked-income", {}).get("targets", []) or []: + errors += site_errors(sid, "mining_modes.banked-income.targets", "bank") + + # --- 4. per-site execitability: long respawn, wilderness, rate ceiling --- + verbs = [a.get("verb") for a in data.get("actions", [])] + for sid, sdef in sites.items(): + if not isinstance(sdef, dict): + continue + if sdef.get("selectable") is True: + access = str(sdef.get("access", "")).lower() + if "wilderness" in access: + errors.append(f"{rel}: selectable site '{sid}' is in the " + f"Wilderness (PK hazard) — it must be selectable: " + f"false, not silently exposed") + respawn = sdef.get("respawn_seconds") + if isinstance(respawn, (int, float)) and respawn >= 120: + if sdef.get("world_hop") is not True: + errors.append(f"{rel}: selectable site '{sid}' has a " + f"{respawn}s respawn — long-respawn sites need " + f"world_hop: true (the wiki's 120+ ores/hr " + f"assumes hopping worlds); without hopping the " + f"ceiling is ~30 ores/hr") + elif "hop_world" not in verbs: + errors.append(f"{rel}: site '{sid}' declares world_hop: true " + f"but the entry has no hop_world action — " + f"world hopping must be executable") + rolls = sdef.get("rolls_per_ore") + modeled = sdef.get("modeled_ores_per_hour") + if isinstance(rolls, (int, float)) and isinstance(modeled, (int, float)): + ceiling = 3600.0 / (rolls * RUNE_DELAY_MID_S) + if modeled > ceiling * (1 + 1e-9): + errors.append( + f"{rel}: site '{sid}' models {modeled} ores/hr but the " + f"cadence ceiling at rolls_per_ore {rolls} is {ceiling:.0f} " + f"(3600 / (rolls_per_ore x 2.7 s)) — the rate exceeds what " + f"the declared click policy can produce") + + # --- 5. fast-xp headline xp rate consistent with the modelled sites --- + eco = data.get("economy") + xph = eco.get("xp_per_hour") if isinstance(eco, dict) else None + if isinstance(xph, (int, float)): + best = None + for sid in modes.get("fast-xp", {}).get("sites", []) or []: + sdef = sites.get(sid) + if not isinstance(sdef, dict): + continue + rate = sdef.get("modeled_ores_per_hour") or 0 + xpv = (sdef.get("xp") or 0) * rate + if best is None or xpv > best: + best = xpv + if best: + if abs(xph - best) / best > XP_TOLERANCE: + errors.append( + f"{rel}: economy.xp_per_hour {xph} is not the fast-xp " + f"modelled policy rate (expected ~{best:.0f} = fastest " + f"fast-xp site's modelled ores/hr x ore xp; tolerance " + f"{XP_TOLERANCE:.0%})") + return errors + + +def _with_runite(sites): + """Fixture helper: the `good` site set plus a non-selectable Wilderness + runite row (hyphenated id so the declared reference resolves).""" + out = dict(sites) + out["runite-wilderness"] = { + "ore": "Runite ore", "ore_id": 451, "mining_level": 85, "xp": 125, + "rock_count": 2, "respawn_seconds": 720, "rolls_per_ore": 3.0, + "modeled_ores_per_hour": 0, "world_hop": False, "selectable": False, + "inventory_policy": "bank", "access": "Wilderness (PK risk)", + "safety": "out of scope", "route": "not selectable"} + return out + + +def run_selftest(verbose=False): + """Committed regression fixtures for the structured mining gates.""" + errors = [] + + def check(name, passed): + if verbose: + print(f"SELFTEST {'PASS' if passed else 'FAIL'} {name}") + if not passed: + errors.append(f"selftest: {name}") + + canon = dict(CANONICAL_PICKAXES) + good_examples = [ + {"account": "1-Attack/41-Mining", "attack": 1, "mining": 41, + "expected_tier": "rune-pickaxe", "expected_equipped": False}, + {"account": "40-Attack/70-Mining", "attack": 40, "mining": 70, + "expected_tier": "rune-pickaxe", "expected_equipped": True}, + {"account": "5-Attack/30-Mining", "attack": 5, "mining": 30, + "expected_tier": "mithril-pickaxe", "expected_equipped": False}, + ] + sites = { + "al-kharid-powermine": {"ore": "Iron ore", "ore_id": 440, "mining_level": 15, + "xp": 35, "rock_count": 3, "respawn_seconds": 5.4, "rolls_per_ore": 1.33, + "modeled_ores_per_hour": 803, "world_hop": False, "selectable": True, + "inventory_policy": "drop", "access": "combat 29+", "safety": "gate", + "route": "drop in place"}, + "se-varrock-bank": {"ore": "Iron ore", "ore_id": 440, "mining_level": 15, + "xp": 35, "rock_count": 2, "respawn_seconds": 5.4, "rolls_per_ore": 1.33, + "modeled_ores_per_hour": 713, "world_hop": False, "selectable": True, + "inventory_policy": "bank", "access": "safe", "safety": "none", + "route": "east Varrock bank 30s"}, + "al-kharid-south-adamantite": {"ore": "Adamantite ore", "ore_id": 449, + "mining_level": 70, "xp": 95, "rock_count": 2, "respawn_seconds": 240, + "rolls_per_ore": 2.0, "modeled_ores_per_hour": 120, "world_hop": True, + "selectable": True, "inventory_policy": "bank", "access": "south rocks", + "safety": "banked locally", "route": "bank 25s + hop_world"}, + } + good = {"pickaxe_tiers": canon, "pickaxe_examples": good_examples, + "mining_modes": {"fast-xp": {"inventory_policy": "drop", + "ore_policy": "iron 15+", + "sites": ["al-kharid-powermine"]}, + "banked-income": {"inventory_policy": "bank", + "targets": ["se-varrock-bank", + "al-kharid-south-adamantite"], + "stop": "gp target"}}, + "mine_sites": sites, + "actions": [{"id": "a1", "step": "hop-world", "verb": "hop_world", + "on_fail": "retry"}], + "economy": {"xp_per_hour": 28100}} + check("canonical pickaxe tiers + recomputed examples pass", + mining_model_errors("ok.yaml", good) == []) + + # pickaxe tier value mismatch + bad_tiers = dict(canon) + bad_tiers["rune-pickaxe"] = {"mining_level": 41, "attack_level": 45, "roll_ticks": 3} + errs = mining_model_errors("bad.yaml", {**good, "pickaxe_tiers": bad_tiers}) + check("rune equip gate value 45 != canonical 40 is rejected", + any("rune-pickaxe" in e and "attack_level" in e for e in errs)) + + # 1-Attack/41-Mining must carry rune — declared expectation is enforced + bad_141 = {"account": "1-Attack/41-Mining", "attack": 1, "mining": 41, + "expected_tier": "rune-pickaxe", "expected_equipped": True} + errs = mining_model_errors("bad2.yaml", {**good, "pickaxe_examples": [bad_141]}) + check("1-Attack/41-Mining example expecting equipped=True is rejected (equip gate: Attack 1 < 40)", + any("equipped=True" in e and "equipped=False" in e for e in errs)) + errs = mining_model_errors("bad3.yaml", {**good, "pickaxe_examples": [ + {"account": "40-Attack/70-Mining", "attack": 40, "mining": 70, + "expected_tier": "rune-pickaxe", "expected_equipped": True}]}) + check("missing the 1-Attack/41-Mining example is rejected (owner-mandated)", + any("1-Attack/41-Mining" in e for e in errs)) + + # mode contract + errs = mining_model_errors("bad4.yaml", {**good, "mining_modes": { + "fast-xp": {"inventory_policy": "bank", + "ore_policy": "iron", "sites": ["al-kharid-powermine"]}, + "banked-income": {"inventory_policy": "bank", "targets": ["se-varrock-bank"], + "stop": "gp target"}}}) + check("fast-xp mode with inventory_policy bank is rejected", + any("fast-xp.inventory_policy" in e for e in errs)) + errs = mining_model_errors("bad5.yaml", {**good, "mining_modes": { + "fast-xp": {"inventory_policy": "drop", "ore_policy": "iron", + "sites": ["al-kharid-powermine"]}, + "banked-income": {"inventory_policy": "bank", + "targets": ["runite-wilderness"], "stop": "gp target"}}, + "mine_sites": _with_runite(sites)}) + check("banked-income targeting a selectable:false site is rejected", + any("selectable: false" in e for e in errs)) + errs = mining_model_errors("bad6.yaml", {**good, "mining_modes": { + "fast-xp": {"inventory_policy": "drop", "ore_policy": "iron", + "sites": ["nope-mine"]}, + "banked-income": {"inventory_policy": "bank", "targets": ["se-varrock-bank"], + "stop": "gp target"}}}) + check("mode referencing an undeclared site is rejected", + any("not declared in mine_sites" in e for e in errs)) + + # long-respawn / wilderness / hop executability + no_hop = {**good, "mine_sites": dict(sites, al_kharid_south_adamantite={ + **sites["al-kharid-south-adamantite"], "world_hop": False})} + errs = mining_model_errors("bad7.yaml", no_hop) + check("selectable 240s-respawn site without world_hop is rejected", + any("world_hop: true" in e for e in errs)) + hop_no_action = {**good, "actions": [{"id": "a1", "step": "mine-stuff", + "verb": "click_object", "on_fail": "retry"}]} + errs = mining_model_errors("bad8.yaml", hop_no_action) + check("world_hop site without a hop_world action is rejected", + any("hop_world action" in e for e in errs)) + wild = {**good, "mine_sites": dict(sites, wildy={"ore": "Runite ore", + "ore_id": 451, "mining_level": 85, "xp": 125, "rock_count": 2, + "respawn_seconds": 720, "rolls_per_ore": 3.0, "modeled_ores_per_hour": 0, + "world_hop": False, "selectable": True, "inventory_policy": "bank", + "access": "Wilderness level 46 (PK risk)", "safety": "none", + "route": "wilderness"})} + errs = mining_model_errors("bad9.yaml", wild) + check("selectable Wilderness site is rejected", + any("Wilderness" in e and "selectable" in e for e in errs)) + + # rate ceiling + over = {**good, "mine_sites": dict(sites, al_kharid_south_adamantite={ + **sites["al-kharid-south-adamantite"], "modeled_ores_per_hour": 839})} + errs = mining_model_errors("bad10.yaml", over) + check("adamantite 839 ores/hr exceeds the 2.0-roll cadence ceiling (~666) and is rejected", + any("cadence ceiling" in e for e in errs)) + + # headline xp consistency + errs = mining_model_errors("bad11.yaml", {**good, "economy": {"xp_per_hour": 32400}}) + check("xp_per_hour 32,400 != modelled fast-xp rate (~28,105) is rejected", + any("xp_per_hour" in e for e in errs)) + errs = mining_model_errors("ok2.yaml", {**good, "economy": {"xp_per_hour": 28000}}) + check("xp_per_hour 28,000 within 2% of the modelled fast-xp rate 28,105 passes", + errs == []) + + return errors + + def main(): global STALE_DAYS if "--stale-days" in sys.argv: STALE_DAYS = int(sys.argv[sys.argv.index("--stale-days") + 1]) errors, warnings = [], [] + if "--selftest" in sys.argv: + errors += run_selftest(verbose=True) + print(f"RESULT: selftest, {len(errors)} errors, 0 warnings") + return 1 if errors else 0 entries = load_entries() if not entries: print("ERROR: no codex entries found under codex/") @@ -107,6 +469,9 @@ def main(): if len(a_steps) != len(set(a_steps)): errors.append(f"{rel}: duplicate action step names") + # 2b. structured mining-model gates (pickaxe gates + run-mode contract) + errors += mining_model_errors(rel, data) + # 3. staleness / research TODOs if data.get("status") == "verified": try: @@ -125,30 +490,6 @@ def main(): f"{rel}: best_practice '{line.get('item') or line.get('name')}' " f"carries a 'researched' source — citation TODO, file follow-up") - # 4. pickaxe gate distinction + mining run-mode contract - # (owner guidance 2026-08-28): the Mining level decides WHICH - # pickaxe can be USED; the Attack level decides ONLY whether it - # can be EQUIPPED. Any entry modelling pickaxes must express both - # gates separately; a mining entry must additionally pin the - # 1-Attack/41-Mining carry-and-use example (rune pickaxe usable at - # 41 Mining, equipable only at 40 Attack) and expose BOTH run - # modes (fast-xp powermine / banked-income). - blob = (yaml.safe_dump(data, sort_keys=False) or "").lower() - if "pickaxe" in blob: - if "mining level" not in blob: - errors.append(f"{rel}: pickaxe model misses the USABILITY gate — " - f"Mining level decides which pickaxe can be used") - if "attack" not in blob: - errors.append(f"{rel}: pickaxe model misses the EQUIP gate — " - f"Attack decides only whether the pickaxe can be equipped") - if "mining" in eid: - if "1-attack/41-mining" not in blob: - errors.append(f"{rel}: pickaxe gates lack the pinned 1-Attack/41-Mining " - f"example (a rune pickaxe carried and used at 1 Attack / 41 Mining)") - if "fast-xp" not in blob or "banked-income" not in blob: - errors.append(f"{rel}: mining entry must expose BOTH run modes " - f"(fast-xp powermine and banked-income) — owner decision 2026-08-28") - # report for w in warnings: print(f"WARN {w}") From 64b5e4890eed861a4b8578a743593686e1032e5d Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 28 Aug 2026 15:49:44 -0500 Subject: [PATCH 6/7] =?UTF-8?q?codex:=20mining-f2p=20=E2=80=94=20executabl?= =?UTF-8?q?e=20GE=20purchase=20loop,=20structured=20copper/tin=20below-15?= =?UTF-8?q?=20fallback,=20a2=20target=20restrict=20(bounce-fix=205)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Governor bounce 5 (2026-08-28) repaired finding by finding: 1. Tool purchase is now executable end to end: new a10 ge_open before any ge_buy; a12 wait-ge-fill is BOUNDED (one 3-6 min fill window) and on no-fill/partial-fill TIMEOUT cancels the resting offer -> a13 collects/refunds -> fail closed via recover (3 loops -> failed); a resting offer is never re-placed indefinitely. Actions renumbered a11..a17. 2. min_level 1 copper/tin below-15 path now has STRUCTURED fallback rows (mine_sites se-varrock-copper / se-varrock-tin, level 1, 17.5 xp, safe East Varrock) added to mining_modes.fast-xp.sites; level-1 and level-14 selections are pinned in a new mining_selections block and recomputed by a new select_fast_site() validator gate (+ 3 committed selftest fixtures). 3. a2 no longer advertises silver/coal/gold/mithril banked targets: it restricts banked-income to the declared rows (iron @ se-varrock-bank, adamantite @ al-kharid-south-adamantite); superheat-coal technique is marked informational-only. New ge_open/ge_offers_done/ge_collect validator gate (+ 1 fixture). Modeled below-15 rate 6,300 XP/hr (was the unbacked '~8,800' prose) recomputed from the bronze cadence midpoint. Validator: 2 entries, 0 errors, 1 pre-existing WARN; --selftest 17 PASS; git diff --check clean. --- codex/training/mining-f2p.yaml | 181 +++++++++++++++++++++++---------- schemas/entry.schema.json | 20 ++++ tools/validate.py | 150 ++++++++++++++++++++++++++- 3 files changed, 295 insertions(+), 56 deletions(-) diff --git a/codex/training/mining-f2p.yaml b/codex/training/mining-f2p.yaml index 1cff11f..1c6677a 100644 --- a/codex/training/mining-f2p.yaml +++ b/codex/training/mining-f2p.yaml @@ -3,7 +3,7 @@ title: Mining (F2P) domain: training tags: [mining, gathering, f2p, skill-training] status: verified -version: 5 # 5: bounce-fix 4 2026-08-28 — structured mine_sites/pickaxe_tiers/pickaxe_examples/mining_modes data model (validated exactly by the validator: no phrase-search); per-site respawn/depletion modeling (adamantite = respawn-bound + world-hop policy, runite NOT selectable — Wilderness PK risk); success-model rolls_per_ore; executable tool prep (check/withdraw/buy/wait/collect/equip via Wield); hop_world action; sub-29 fast-xp stays fast-xp at a safe drop site; modeled rates recomputed (28,000 xp/hr fast-xp drop; 70,080 gp/hr adamantite w/ hop; 49,900 iron banked) +version: 6 # 6: bounce-fix 5 2026-08-28 — executable GE purchase loop (new a10 ge_open action; bounded fill/no-fill timeout -> cancel/refund/collect -> fail closed, never re-place a resting offer); structured copper/tin below-15 fallback sites (mine_sites se-varrock-copper / se-varrock-tin, level 1, 17.5 xp, safe) pinned in mining_selections (levels 1 & 14) with validator fixtures; a2 restricted to the DECLARED banked-income target rows (iron @ se-varrock-bank, adamantite @ al-kharid-south-adamantite only — silver/coal/gold/mithril no longer advertised) updated: "2026-08-28" # ISO dates must stay quoted — YAML 1.1 parses bare dates to datetime.date summary: > @@ -11,10 +11,13 @@ summary: > validated against the structured mine_sites table; the entry never silently picks "the highest available rock": - fast-xp: powermining for XP. Iron (level 15, 35 xp) from level 15 - onward, copper/tin (17.5 xp) below 15; every full inventory is DROPPED - (drop route explicit — drop_run). Sub-combat-29 accounts run fast-xp at - the safe South-east Varrock drop site; combat 29+ may use the Al Kharid - 3-rock powermine. Mode NEVER switches to banking. + onward; copper/tin (17.5 xp) below 15 run on STRUCTURED fallback + mine_sites rows (se-varrock-copper / se-varrock-tin, East Varrock, + level 1, safe — selection pinned in mining_selections at levels 1 and + 14). Every full inventory is DROPPED (drop route explicit — drop_run). + Sub-combat-29 accounts run fast-xp at the safe South-east Varrock drop + site; combat 29+ may use the Al Kharid 3-rock powermine. Mode NEVER + switches to banking. - banked-income: ONE deterministic target mine_sites row chosen and validated at session start (Mining level >= ore level; the row's access/safety gates; inventory policy = bank) and every load is BANKED @@ -37,6 +40,7 @@ accounts: - "Pickaxe: two SEPARATE gates per tier (pickaxe_tiers table): MINING level decides which pickaxe can be USED (bronze 1, iron 1, steel 6, mithril 21, adamant 31, rune 41); ATTACK level decides ONLY whether it can be EQUIPPED (steel 5, mithril 20, adamant 30, rune 40). Select by Mining, equip only when Attack permits, otherwise CARRY the same selected tool — a 1-Attack/41-Mining account selects, carries, and uses a rune pickaxe (pickaxe_examples)" - "Run mode chosen at session start: fast-xp (powermine, drop every load) or banked-income (deterministic target mine_sites row, bank every load) — mining_modes table" - "Bank route from the chosen mine (fast-xp drops instead when no bank leg is wanted)" + - "Below-15 fast-xp (Mining 1-14): copper/tin (17.5 xp) at the structured East Varrock fallback rows (mine_sites se-varrock-copper / se-varrock-tin — no aggressive NPCs, safe at any combat level); the deterministic selection is pinned in mining_selections (levels 1 and 14) and recomputed by the validator" - "Al Kharid powermine HARD GATE: combat level >= 29 (a monster attacks players at <= 2x its level; the scorpion is level 14, so its aggro ceiling is combat 28 and 29+ is safe). Sub-combat-29 accounts run fast-xp at the South-east Varrock DROP site (no aggressive NPCs) — the mode does not change to banking" - "adamantite @ Al Kharid south REQUIRES world hopping (mine_sites row: world_hop true, hop_world action) — respawns are 4 min and the wiki's 120+ ores/hr assumes hopping F2P worlds" @@ -53,7 +57,7 @@ best_practice: why: "the quest reward alone takes Mining from 1 to 10; the materials (6 clay, 4 copper, 2 iron) mine at level 1 except iron, which can be bought on the GE" source: "OSRS wiki — Free-to-play Mining training (Level 1-10: Doric's Quest)" - name: "powermine iron for maximum XP (fast-xp only)" - why: "iron (level 15, 35 xp) is the fastest F2P training ore; Al Kharid is the only F2P site with three iron rocks around one tile (combat 29+ gate) and South-east Varrock's two iron rocks serve sub-29 accounts with the same drop behaviour — the mode is unchanged. Modeled fast-xp rate ~28,000 XP/hr at 61+ with rune (success model + drop loop + breaks pinned in economy); drop the ores, banking costs XP" + why: "iron (level 15, 35 xp) is the fastest F2P training ore; Al Kharid is the only F2P site with three iron rocks around one tile (combat 29+ gate) and South-east Varrock's two iron rocks serve sub-29 accounts with the same drop behaviour — the mode is unchanged. Below 15 the structured copper/tin fallback (se-varrock-copper / se-varrock-tin, 17.5 xp, safe East Varrock) keeps the same drop behaviour for level 1-14 (mining_selections pins levels 1 and 14). Modeled fast-xp rate ~28,000 XP/hr at 61+ with rune (success model + drop loop + breaks pinned in economy); drop the ores, banking costs XP" source: "OSRS wiki — Free-to-play Mining training (Levels 15-99: Iron ore); OSRS wiki — Aggressiveness; OSRS wiki — Iron rocks (respawn ~5.4 s)" - name: "bank iron at the South-east Varrock mine (banked-income anchor)" why: "the guide names this F2P banking location explicitly; the eastern Varrock bank is the closest bank leg (~30 s per load), and the ores feed Smithing. Modeled ~49,900 gp/hr with rune (midpoint policy model, see economy comments)" @@ -64,15 +68,18 @@ best_practice: - name: "runite is NOT selectable in this entry" why: "F2P runite sits in the Wilderness with a ~12-minute respawn — modelling it would require Wilderness travel and PK survival plus heavy world hopping; the mine_sites row is selectable: false and the entry refuses it rather than hand-waving the hazard" source: "OSRS wiki — Runite ore (respawn ~12 min; F2P rocks in the Wilderness); mine_sites row runite-wilderness" - - name: "superheat coal (optional banked variant): West Lumbridge Swamp mine" - why: "coal (level 30, 50 xp) banks reasonably from the Lumbridge Swamp mine; with 43 Magic, Superheat Item converts coal into bars without a furnace, cutting bank trips (staff of fire + nature runes)" - source: "OSRS wiki — Free-to-play Mining training (Levels 30-99: Superheat Item)" + - name: "superheat coal (informational only — NOT a selectable banked-income target)" + why: "the West Lumbridge Swamp mine holds coal (level 30, 50 xp); with 43 Magic, Superheat Item converts coal to bars without a furnace. This entry does NOT declare a coal mine_sites row: banked-income targets are iron and adamantite ONLY, so superheat-coal is background knowledge (staff of fire + nature runes), never an advertised or executable target — a2's target set matches mining_modes.banked-income.targets exactly" + source: "OSRS wiki — Free-to-play Mining training (Levels 30-99: Superheat Item, background note only)" - name: "worked example — fast-xp session (preparation -> first full inventory -> first break -> resume)" - why: "Account: Mining 41, Attack 1, combat 35 (passes the Al Kharid gate). (1) prepare: mode fast-xp; ore policy iron 15+; pickaxe by MINING level -> rune (41 <= 41); equip gate Attack 1 < 40 -> CARRY the same tool (27 ore slots); the rune pickaxe is owned, so acquire = a9 withdraw-tool (bank_inventory); initial inventory = pickaxe + empty ore space. (2) travel: walk to Al Kharid (combat 35 >= 29 OK) and position at the 3-rock cluster. (3) mining rotations: click a fresh iron rock (a2, rune cadence [1800, 3600] ms) -> wait_rock: ore gained after ~1.33 rolls (success model) -> mine; repeat until the inventory is full. (4) first full-inventory boundary: 27 ores -> drop_run: 27 executions of a6 (right_click Drop, [200, 800] ms each) -> count_item ore == 0 -> mine. (5) first scheduled break: after the 8th-14th rotation the break counter trips -> break_state: take_break 60-180 s (a15). (6) resume: reposition at the cluster (a16) -> mine; the loop repeats until the XP target or session cap -> complete. Modeled policy rate: 803 ores/hr = 28,105 XP/hr -> pin 28,000 (economy)" + why: "Account: Mining 41, Attack 1, combat 35 (passes the Al Kharid gate). (1) prepare: mode fast-xp; ore policy iron 15+; pickaxe by MINING level -> rune (41 <= 41); equip gate Attack 1 < 40 -> CARRY the same tool (27 ore slots); the rune pickaxe is owned, so acquire = a9 withdraw-tool (bank_inventory); initial inventory = pickaxe + empty ore space. (2) travel: walk to Al Kharid (combat 35 >= 29 OK) and position at the 3-rock cluster. (3) mining rotations: click a fresh iron rock (a2, rune cadence [1800, 3600] ms) -> wait_rock: ore gained after ~1.33 rolls (success model) -> mine; repeat until the inventory is full. (4) first full-inventory boundary: 27 ores -> drop_run: 27 executions of a6 (right_click Drop, [200, 800] ms each) -> count_item ore == 0 -> mine. (5) first scheduled break: after the 8th-14th rotation the break counter trips -> break_state: take_break 60-180 s (a16). (6) resume: reposition at the cluster (a17) -> mine; the loop repeats until the XP target or session cap -> complete. Modeled policy rate: 803 ores/hr = 28,105 XP/hr -> pin 28,000 (economy)" source: "derived from this entry's policy model (state_machine prepare/travel/mine/wait_rock/drop_run/break_state/complete + actions + run_control.break + mine_sites/rolls_per_ore), synthesized 2026-08-28" - name: "worked example — banked-income adamantite session (preparation -> first full inventory -> first break -> resume)" - why: "Account: Mining 70, Attack 40, combat 60. Target: 500,000 gp banked or a 2 h session cap. (1) prepare: mode banked-income; target row = al-kharid-south-adamantite (mine_sites: ore level 70 <= Mining 70 OK; access/route validated; inventory policy bank) — the row id is logged to activity state; pickaxe by MINING level -> rune (41 <= 70); equip gate Attack 40 >= 40 -> EQUIP (28 ore slots); tool owned, withdraw from bank (a9). (2) travel: walk to the Al Kharid south adamantite spot. (3) mining rotations: click a fresh adamantite rock (rune roll 1.8 s, delay [1800, 3600] ms) -> wait_rock: ore gained -> mine; when BOTH rocks are depleted -> hop: hop_world (a14) to a fresh F2P world (world_hop row) and continue — this is where the wiki's 120 ores/hr comes from. (4) first full-inventory boundary: 28 ores -> bank_run: walk to the Al Kharid bank (a3), deposit all 28 (a4; 28 x 584 = 16,352 gp logged), walk back (a5) -> mine. (5) first scheduled break: after 8-14 rotations -> break_state: take_break 60-180 s (independent of the bank trips). (6) resume: reposition (a16) -> mine; complete when the banked value >= 500,000 gp or the session cap hits" + why: "Account: Mining 70, Attack 40, combat 60. Target: 500,000 gp banked or a 2 h session cap. (1) prepare: mode banked-income; target row = al-kharid-south-adamantite (mine_sites: ore level 70 <= Mining 70 OK; access/route validated; inventory policy bank) — the row id is logged to activity state; pickaxe by MINING level -> rune (41 <= 70); equip gate Attack 40 >= 40 -> EQUIP (28 ore slots); tool owned, withdraw from bank (a9). (2) travel: walk to the Al Kharid south adamantite spot. (3) mining rotations: click a fresh adamantite rock (rune roll 1.8 s, delay [1800, 3600] ms) -> wait_rock: ore gained -> mine; when BOTH rocks are depleted -> hop: hop_world (a15) to a fresh F2P world (world_hop row) and continue — this is where the wiki's 120 ores/hr comes from. (4) first full-inventory boundary: 28 ores -> bank_run: walk to the Al Kharid bank (a3), deposit all 28 (a4; 28 x 584 = 16,352 gp logged), walk back (a5) -> mine. (5) first scheduled break: after 8-14 rotations -> break_state: take_break 60-180 s (independent of the bank trips). (6) resume: reposition (a17) -> mine; complete when the banked value >= 500,000 gp or the session cap hits" source: "derived from this entry's policy model (state_machine prepare/travel/mine/wait_rock/hop/bank_run/break_state/complete + actions + mine_sites.al-kharid-south-adamantite), synthesized 2026-08-28" + - name: "worked example — level-1 fast-xp copper/tin session (preparation -> first full inventory -> first break -> resume)" + why: "Account: Mining 1, Attack 1, combat 3. (1) prepare: mode fast-xp; below-15 selection pinned at level 1 (mining_selections) -> se-varrock-copper / se-varrock-tin; pickaxe by MINING -> the canonical selection at Mining 1 (bronze; iron shares the Mining-1 gate and can be substituted once owned); equip gate Attack 1 -> no equip, CARRY; the tool is owned, so acquire = a9 withdraw-tool (bank_inventory). (2) travel: walk to the East Varrock copper/tin rocks (safe — no aggressive NPCs) and position at the cluster. (3) mining rotations: click a fresh copper or tin rock (a2, bronze cadence [4800, 6600] ms) -> wait_rock: ore gained after ~1.6 rolls (success model) -> mine; both ores are 17.5 xp each. (4) first full-inventory boundary: 27 ores -> drop_run: a6 drops ONE slot per execution (right_click Drop, [200, 800] ms), looping until count_item ore == 0 -> mine. (5) first scheduled break: after the 8th-14th rotation the break counter trips -> break_state: take_break 60-180 s (a16). (6) resume: reposition at the cluster (a17) -> mine; complete on the XP/level target. Modeled policy rate: ~360 ores/hr = 6,300 XP/hr at the bronze cadence midpoint (economy)." + source: "derived from this entry's policy model (mining_selections levels 1/14 + mine_sites se-varrock-copper / se-varrock-tin + state_machine + actions a9/a2/a6/a16/a17), synthesized 2026-08-28" pickaxe_tiers: bronze-pickaxe: @@ -120,13 +127,26 @@ pickaxe_examples: expected_tier: mithril-pickaxe expected_equipped: false # 30 Mining selects mithril (21); Attack 5 < 20 -> CARRY +# Deterministic mode-selection proof (governor bounce 5, 2026-08-28): the +# validator recomputes every row from mining_modes + mine_sites (eligible = +# selectable fast-xp site whose mining_level <= level; choose the highest +# ore xp, tie-break lexicographic site id) and compares. The level-1 and +# level-14 rows prove a below-15 account can always arm a structured +# copper/tin fast-xp run (mirrors the pickaxe_examples recompute pattern). +mining_selections: + fast_xp: + - level: 1 + expected_site: se-varrock-copper # copper & tin share the East Varrock rocks (17.5 xp) + - level: 14 + expected_site: se-varrock-copper # iron needs 15, so 1-14 stays on the copper/tin fallback + # Mode contract (owner decision 2026-08-28): both modes explicit; each mode's # sites/targets reference mine_sites rows; inventory policies are fixed. mining_modes: fast-xp: inventory_policy: drop # powermine: every load dropped, never banked - ore_policy: "iron (level 15, 35 xp) from 15 onward; copper/tin (17.5 xp) below 15 — never highest-available; sub-combat-29 accounts DROP at the South-east Varrock site (safe), combat 29+ may use the Al Kharid powermine — the mode never switches to banking" - sites: [al-kharid-powermine, se-varrock-drop] + ore_policy: "Mining 15+: iron (level 15, 35 xp) — never highest-available; Mining 1-14: copper/tin (17.5 xp) at the structured fallback rows (mine_sites se-varrock-copper / se-varrock-tin, level 1, safe); sub-combat-29 accounts DROP at the South-east Varrock site (safe), combat 29+ may use the Al Kharid powermine — the mode never switches to banking; site selection pinned in mining_selections (levels 1 and 14)" + sites: [al-kharid-powermine, se-varrock-drop, se-varrock-copper, se-varrock-tin] banked-income: inventory_policy: bank # every load banked until the GP target targets: [se-varrock-bank, al-kharid-south-adamantite] @@ -168,6 +188,36 @@ mine_sites: access: "no aggressive NPCs — safe at any combat level (the sub-29 fast-xp site)" safety: "none required" route: "drop in place — no bank leg" + se-varrock-copper: + ore: Copper ore + ore_id: 436 + mining_level: 1 + xp: 17.5 + rock_count: 2 + respawn_seconds: 5.4 + rolls_per_ore: 1.6 # below-15 success model (lower than iron 61+ = 1.33) + modeled_ores_per_hour: 360 # bronze cadence midpoint 5.7 s: 1.6 x 5.7 + 0.5 + 0.39 = 10.01 s/ore (economy) + world_hop: false + selectable: true + inventory_policy: drop + access: "East Varrock copper/tin rocks — no aggressive NPCs; safe at any combat level" + safety: "none required" + route: "drop in place; tin stands at the same rocks (se-varrock-tin) — a below-15 fast-xp run mines both copper and tin (17.5 xp each)" + se-varrock-tin: + ore: Tin ore + ore_id: 438 + mining_level: 1 + xp: 17.5 + rock_count: 2 + respawn_seconds: 5.4 + rolls_per_ore: 1.6 + modeled_ores_per_hour: 360 + world_hop: false + selectable: true + inventory_policy: drop + access: "East Varrock tin/copper rocks — no aggressive NPCs; safe at any combat level" + safety: "none required" + route: "drop in place; copper stands at the same rocks (se-varrock-copper) — a below-15 fast-xp run mines both copper and tin (17.5 xp each)" se-varrock-bank: ore: Iron ore ore_id: 440 @@ -192,7 +242,7 @@ mine_sites: respawn_seconds: 240 # wiki F2P adamantite guide: ~4-min respawn rolls_per_ore: 2.0 # conservative success input at 70 Mining (rate is respawn-bound, not success-bound) modeled_ores_per_hour: 120 # Sourced measured baseline: wiki money guide ~120 ores/hr WITH world hopping; no-hop ceiling ~30 ores/hr - world_hop: true # hop_world required (a14); the modelled rate assumes it + world_hop: true # hop_world required (a15); the modelled rate assumes it selectable: true inventory_policy: bank access: "Al Kharid south rocks (no aggressive NPCs at this cluster)" @@ -241,8 +291,10 @@ economy: # banked adamantite @ Al Kharid south (hop): 120 ores/hr (sourced # measured baseline) x 584 = 70,080 gp/hr; no-hop ceiling ~30 ores/hr # = 17,520 gp/hr — the entry models ONLY the hop rate. - # bronze path (below 15, copper/tin 17.5 xp): cadence midpoint 5.7 s -> - # ~8,800 XP/hr. + # copper/tin path (Mining 1-14, 17.5 xp): cadence midpoint 5.7 s + # (bronze [4800, 6600]) -> 1.6 rolls x 5.7 + 0.5 drop + 0.39 break = + # 10.01 s/ore -> 360 ores/hr -> 6,300 XP/hr (mine_sites + # se-varrock-copper / se-varrock-tin). # EXTERNAL BENCHMARK (NOT the modeled rate): the wiki's 50,000 XP/hr # (41+) / 52,500 XP/hr (61+) iron powermine figures assume a continuous # 3-tick rotation with zero delay/drop/break overhead; the configured @@ -265,6 +317,7 @@ economy: - "F2P ore chain (wiki guide): clay 1/5xp, copper 1/17.5, tin 1/17.5, iron 15/35, silver 20/40, coal 30/50, gold 40/65, mithril 55/80, adamantite 70/95, runite 85/125" - "Sell prices 2026-08-27 (low edge): iron 70, coal 151, silver 73, gold 154, mithril 159, adamantite 584, runite 10,265" - "fast-xp: powermine iron 15+ and drop every load — XP is the product, realized gp 0; modeled 803 ores/hr -> 28,105 XP/hr (pin 28,000) at 61+ rune" + - "fast-xp 1-14 (copper/tin 17.5 xp): modeled ~360 ores/hr -> 6,300 XP/hr at the bronze cadence midpoint (economy comment) — structured rows se-varrock-copper / se-varrock-tin, pinned at levels 1 and 14 in mining_selections" - "banked-income iron @ SE Varrock: modeled 713 ores/hr -> 49,910 gp/hr (pin 49,900); deterministic target row (mine_sites.se-varrock-bank)" - "banked-income adamantite @ Al Kharid south: respawn-BOUND site (2 rocks, 4-min respawn) — modeled 120 ores/hr ONLY with world hopping (hop_world) = 70,080 gp/hr; without hopping the ceiling is ~30 ores/hr = 17,520 gp/hr (wiki F2P adamantite money guide)" - "runite is NOT a selectable target in this entry — F2P runite sits in the Wilderness (PK risk, ~12-min respawn); the mine_sites row is selectable: false" @@ -277,22 +330,25 @@ state_machine: goal: >- Session start. 1) Choose the run mode (mining_modes): fast-xp (powermine, drop) or banked-income (deterministic target mine_sites - row, bank). 2) fast-xp ore policy: iron 15+ (copper/tin below 15), - site from mining_modes.fast-xp.sites (combat 29+ -> Al Kharid - powermine, else SE Varrock drop — SAME mode, never banking). - banked-income: pick ONE row from mining_modes.banked-income.targets - and validate: level_is Mining >= row mining_level, the row's access - gates hold (e.g. adamantite requires the hop policy), inventory - policy == bank; the row id is logged to activity state. 3) Pickaxe: - select the highest pickaxe_tiers row whose mining_level the account - meets (rune is usable at 41 Mining and equipable at 40 Attack). - 4) Equip ONLY when level_is Attack >= the row's attack_level; - otherwise carry the same selected tool (a 1-Attack/41-Mining account - selects and CARRIES a rune pickaxe). 5) Acquire the tool if missing - (a8-a12: check / withdraw-owned / buy / wait / collect) and build - the initial inventory: pickaxe (equipped or carried) + empty ore - space; no food (all routes gate-safe). 6) Record mode + target row - id in activity state. + row, bank). 2) fast-xp ore/site selection (deterministic; pinned in + mining_selections at levels 1 and 14): Mining 15+ -> iron site from + mining_modes.fast-xp.sites (combat 29+ -> Al Kharid powermine, else + SE Varrock drop — SAME mode, never banking); Mining 1-14 -> the + structured copper/tin fallback (mine_sites se-varrock-copper / + se-varrock-tin, 17.5 xp, safe). banked-income: pick ONE row from + mining_modes.banked-income.targets and validate: level_is Mining >= + row mining_level, the row's access gates hold (e.g. adamantite + requires the hop policy), inventory policy == bank; the row id is + logged to activity state. 3) Pickaxe: select the highest + pickaxe_tiers row whose mining_level the account meets (rune is + usable at 41 Mining and equipable at 40 Attack). 4) Equip ONLY when + level_is Attack >= the row's attack_level; otherwise carry the same + selected tool (a 1-Attack/41-Mining account selects and CARRIES a + rune pickaxe). 5) Acquire the tool if missing (a8-a13: check / + withdraw-owned / open GE / place offer / bounded wait+collect) and + build the initial inventory: pickaxe (equipped or carried) + empty + ore space; no food (all routes gate-safe, incl. the copper/tin + fallback). 6) Record mode + target row id in activity state. next: [travel, complete, failed] travel: goal: "walk/teleport to the selected mine_sites location (fast-xp: Al Kharid 3-rock powermine at combat 29+, else South-east Varrock; banked-income: the session's validated target row) and position at the target cluster; arrive with the pickaxe equipped or carried per the prepare gate" @@ -301,9 +357,10 @@ state_machine: goal: >- Mine the session target rock until the inventory is full or the run target is reached. Rock selection is MODE-DRIVEN and deterministic: - fast-xp mines iron (15+) or copper/tin (<15); banked-income mines - ONLY the session's target mine_sites row (never the highest - available rock). Every a2 click is followed by the wait_rock outcome + fast-xp mines iron (15+) or copper/tin (<15, the structured + se-varrock fallback rows); banked-income mines ONLY the session's + target mine_sites row (never the highest available rock). Every a2 + click is followed by the wait_rock outcome wait — never chain clicks on the same rock. A world_hop site (adamantite) hops when both rocks are depleted. next: [wait_rock, drop_run, bank_run, hop, recover, survive, complete] @@ -311,7 +368,7 @@ state_machine: goal: "after clicking a rock (a2), wait for the mining outcome — ore gained (may take several rolls: rolls_per_ore), rock depleted, or roll timeout (2x the equipped tier's roll) — before any re-click; never re-click the active rock while its roll is unresolved; respawns of the site's rock_count rocks are absorbed here (wiki respawn figures in mine_sites)" next: [mine, hop, drop_run, bank_run, recover] hop: - goal: "world_hop sites ONLY (al-kharid-south-adamantite): both target rocks are depleted -> hop_world (a14) to a fresh F2P world and continue mining its fresh rocks; hop count tracked; after 8 consecutive hops with no ore gained, go to recover (possible world lockout / price-stale)" + goal: "world_hop sites ONLY (al-kharid-south-adamantite): both target rocks are depleted -> hop_world (a15) to a fresh F2P world and continue mining its fresh rocks; hop count tracked; after 8 consecutive hops with no ore gained, go to recover (possible world lockout / price-stale)" next: [mine, recover] drop_run: goal: "fast-xp ONLY: drop every occupied ore slot — one right-click Drop per slot (a6 re-executed), looping until count_item ore == 0. Slot count is DYNAMIC: inventory capacity minus non-ore items (equipped pickaxe = 28 ore slots, carried = 27, any session load = fewer) — never a hardcoded loop count" @@ -320,10 +377,10 @@ state_machine: goal: "banked-income ONLY: walk to the nearest bank, deposit all ores (activity state: target row id + load value logged), walk back to the mine; the pickaxe stays (equipped or carried) and the inventory is rebuilt as empty ore space for the next load" next: [mine, break_state, complete] break_state: - goal: "scheduled break independent of incidental banking: after every run_control.break.every rotations (8-14) take_break (60-180 s AFK, a15), then reposition at the cluster (a16) and resume mining" + goal: "scheduled break independent of incidental banking: after every run_control.break.every rotations (8-14) take_break (60-180 s AFK, a16), then reposition at the cluster (a17) and resume mining" next: [mine, complete] recover: - goal: "diagnose and fix: no fresh rock of the target tier for 2 respawn cycles (rotate cluster / wait respawn / hop for world_hop rows); invalid location (is_at fails — wrong mine or wrong ore) -> re-travel to the correct mine_sites location; missing tool (has_item pickaxe false) -> acquire (a8-a12) and re-prepare; path failure (move_to on_fail) -> retry travel. Persistent after 3 recoveries -> failed" + goal: "diagnose and fix: no fresh rock of the target tier for 2 respawn cycles (rotate cluster / wait respawn / hop for world_hop rows); invalid location (is_at fails — wrong mine or wrong ore) -> re-travel to the correct mine_sites location; missing tool (has_item pickaxe false) -> acquire (a8-a13) and re-prepare; path failure (move_to on_fail) -> retry travel. Persistent after 3 recoveries -> failed" next: [prepare, travel, mine, failed] survive: goal: "defensive fallback only (the 29+ gate means scorpions never aggro at Al Kharid): if attacked anyway (gate bypassed/griefed), retreat out of the aggro radius and re-enter the cluster when safe; no food loop is modeled" @@ -421,7 +478,7 @@ state_machine: when: "session cap reached during the break" to: complete - from: recover - when: "tool missing -> re-acquired (a8-a12) or withdrawn from bank" + when: "tool missing -> re-acquired (a8-a13) or withdrawn from bank" to: prepare - from: recover when: "wrong location -> re-routed to the correct mine_sites location" @@ -458,7 +515,7 @@ actions: verb: click_object target: "fresh rock of the session target tier — never the rock whose roll is in progress" params: - rock: "MODE-DRIVEN: fast-xp -> iron rock (level 15+; copper/tin below 15); banked-income -> ONLY the session's target mine_sites row (silver 20, coal 30, gold 40, mithril 55, adamantite 70) — never highest-available" + rock: "MODE-DRIVEN: fast-xp -> Mining 15+: iron rock (Al Kharid 3-rock / SE Varrock 2-rock); Mining 1-14: copper/tin rock at the East Varrock fallback (mine_sites se-varrock-copper / se-varrock-tin, 17.5 xp); banked-income -> ONLY the session's declared target row from mining_modes.banked-income.targets (iron @ se-varrock-bank or adamantite @ al-kharid-south-adamantite — the ONLY declared/validated banked targets; no other ore is advertised) — never highest-available" gate: "level_is Mining >= the site row's mining_level (e.g. adamantite = 70), and for banked-income the rock must be at the session's validated mine_sites location" mode_visible: "activity state logs mode + target row id + ore name on every rotation — the target is visible, never implicit" rotation: "the site's rock_count cluster (Al Kharid 3-rock / SE Varrock 2-rock / adamantite 2-rock); click a fresh rock only after the wait_rock outcome wait (rolls_per_ore success model)" @@ -530,7 +587,7 @@ actions: verb: has_item target: "the selected pickaxe tier (Mining-gated: pickaxe_tiers)" params: - branch: "owned -> a9 withdraw-tool (or straight to a13 equip-tool if already in the inventory); missing -> a10 buy-tool (GE purchase path)" + branch: "owned -> a9 withdraw-tool (or straight to a14 equip-tool if already in the inventory); missing -> the GE purchase branch: a10 open-ge -> a11 buy-tool -> a12 wait (bounded fill) -> a13 collect (verify)" verify: "inventory holds exactly one pickaxe of the selected tier — never a second pickaxe" on_fail: skip anti_ban: @@ -548,6 +605,17 @@ actions: delay: [500, 1200] - id: a10 + step: open-ge + verb: ge_open + target: grand-exchange + params: + location: "the GE booth (bank side) — REQUIRED before any ge_buy / ge_collect: the exchange interface must be open before an offer is placed or items are collected" + note: "only the purchase branch reaches the GE (a8 missing-tool); owned tools go straight to a9 withdraw-tool" + on_fail: retry + anti_ban: + delay: [600, 1600] + + - id: a11 step: buy-tool verb: ge_buy target: grand-exchange @@ -555,32 +623,34 @@ actions: item: "the selected pickaxe tier (Mining-gated)" qty: 1 max_price: "GE mid + 10% cap" - pair_with: "a11 wait-ge-fill (offer fill wait) then a12 collect-tool (collect + verify)" + note: "place exactly ONE buy offer; the fill waits at most the BOUNDED a12 window — a resting offer is never retried indefinitely" + pair_with: "a10 open-ge (before) then a12 wait-ge-fill (bounded) then a13 collect-tool (collect + verify)" on_fail: retry anti_ban: delay: [1200, 3000] - - id: a11 + - id: a12 step: wait-ge-fill verb: ge_offers_done target: "the pickaxe purchase offer" params: - until: "the pickaxe offer fills or times out — never proceed with a pending offer" - on_fail: retry + until: "the pickaxe offer FILLS (has_item pickaxe) — poll for exactly ONE bounded fill window (3-6 min); on no-fill / partial-fill TIMEOUT: CANCEL the resting offer, collect the item if it finally filled or REFUND the gp (a13), then verify — fail CLOSED via recover (3 loops -> failed); never re-place the same resting offer" + on_fail: alert anti_ban: delay: [900, 2400] - - id: a12 + - id: a13 step: collect-tool verb: ge_collect target: grand-exchange params: - verify: "has_item pickaxe == true after collection; otherwise recover (a8 path) — a failed purchase or equip is diagnosed, never assumed" + collect: "finished GE transactions — the bought pickaxe if the offer filled, and the gp refund if it was cancelled on timeout; collection closes the offer slot" + verify: "has_item pickaxe == true after collection; if still missing (no-fill / cancelled), diagnose via recover (a8 path, 3 loops -> failed) — a failed purchase is diagnosed, never silently re-queued" on_fail: retry anti_ban: delay: [600, 1400] - - id: a13 + - id: a14 step: equip-tool verb: menu_option target: "the selected pickaxe tier in the inventory" @@ -591,7 +661,7 @@ actions: anti_ban: delay: [600, 1400] - - id: a14 + - id: a15 step: hop-world verb: hop_world target: "a fresh F2P world" @@ -602,7 +672,7 @@ actions: anti_ban: delay: [900, 2200] - - id: a15 + - id: a16 step: take-break verb: take_break target: "self" @@ -613,7 +683,7 @@ actions: anti_ban: delay: [300, 900] - - id: a16 + - id: a17 step: reposition-cluster verb: walk_to target: "cluster center / fresh rock of the target tier" @@ -629,11 +699,12 @@ run_control: max_iterations: null break: every: [8, 14] # rotations; the break counter is independent of incidental banking and hops - for: [60, 180] # seconds of idle AFK (take_break, a15) + for: [60, 180] # seconds of idle AFK (take_break, a16) stop_conditions: - "fast-xp: Mining XP/level target reached (session target); banked-income: banked GP target reached or session cap" - "3 consecutive failed rock clicks (client issue — do not loop)" - "recover looped 3 times: persistent missing tool, invalid location, or path failure" + - "GE purchase fail-closed: the bounded fill window expired and the tool is still missing after cancel/refund/collect (recover looped 3 times) — never re-place a resting buy offer" - "death: hard failure — the Al Kharid hard gate (combat 29+) and the survive (retreat) fallback prevent it in normal operation; if is_dead anyway (gate bypassed/griefed) log ERROR + alert and stop — no respawn loop" risk: @@ -642,13 +713,17 @@ risk: - "Mining is a staple low-risk method: long, repetitive, low-input behaviour that matches normal player sessions — the guide itself recommends hours of drop-mining." - "The powermine rotation is the detection vector if executed perfectly for hours — keep the a2 tier-aware cadence (wait_rock outcome wait + roll_table pause; anchor rune 1.8-3.6 s, never below the equipped tier's full roll) and take the break schedule." - "Al Kharid mine has aggressive level-14 scorpions (they attack players at combat level <= 28 — wiki Aggressiveness). The entry HARD-gates Al Kharid to combat 29+; sub-29 accounts run fast-xp at the South-east Varrock DROP site (no aggressive NPCs, same mode). No food is part of either route; the survive state (retreat) is a defensive fallback only." + - "Below-15 fast-xp stays at the safe East Varrock copper/tin rocks (se-varrock-copper / se-varrock-tin — no aggressive NPCs), so no food or combat gate applies; the mode is unchanged (drop every load)." - "World hopping (adamantite row only) is pace-sensitive: hop at the depletion trigger, not on a timer, and back off after 8 fruitless hops. The modelled 120 ores/hr is the wiki's hopping baseline — never claim it without the hop policy." - - "Bottom line on rates: 28,000 XP/hr (fast-xp) and 70,080 gp/hr (adamantite w/ hop) are this entry's modelled policy rates derived in economy; the wiki's 52,500 XP/hr is a labeled external benchmark, not an achieved rate." + - "Bottom line on rates: 28,000 XP/hr (fast-xp iron 15+) and 6,300 XP/hr (fast-xp copper/tin below 15) are this entry's modelled fast-xp policy rates; 70,080 gp/hr (adamantite w/ hop) is the banked-income modelled rate — all derived in economy; the wiki's 52,500 XP/hr is a labeled external benchmark, not an achieved rate." sources: - "OSRS wiki — Active/passive aggressive monsters: Aggressiveness (a monster attacks players whose combat level is <= 2x its level; a level-14 scorpion is safe from combat level 29) — verified 2026-08-27" - "OSRS wiki — Scorpion (combat level 14, hitpoints 17; Al Kharid mine) — verified 2026-08-27" - "OSRS wiki — Free-to-play Mining training (ore level/xp table — all rows F2P; pickaxe tier table with tick intervals (bronze 8 -> rune 3); Doric's Quest 1,300 xp; iron 50,000 xp/hr at 41+ / 52,500 at 61+ cited as zero-overhead external benchmarks only — the configured midpoint cadence caps the ideal at ~46,667; Al Kharid 3-rock spot; SE Varrock banking; Superheat coal; scorpions noted — this entry hard-gates the mine to combat 29+ instead, so food is not modeled) — verified 2026-08-27" + - "OSRS wiki — Copper ore / Tin ore (level 1, 17.5 XP each, F2P) — the below-15 fast-xp fallback rows (se-varrock-copper / se-varrock-tin) — verified 2026-08-28" + - "OSRS wiki — East of Varrock mine (copper/tin/iron rocks; no aggressive NPCs) — the safe below-15 and sub-29 fast-xp/SE Varrock location — verified 2026-08-28" + - "OSRS wiki — Grand Exchange (buy offers: place at mid, cancel/refund on timeout) — a10-a13 purchase loop — wiki mechanics, verified 2026-08-28" - "OSRS wiki — Money making guide/Mining adamantite ore (F2P) (4-minute respawn; about 120+ ores/hour by hopping worlds only; at the committed 584 gp that is <=17,520 gp/hr without hopping or ~70,080 gp/hr at 120 ores/hr) — verified 2026-08-28" - "OSRS wiki — Iron rocks (respawn approximately 5.4 seconds) — verified 2026-08-28" - "OSRS wiki — Runite ore (respawn about twelve minutes; F2P rocks in the Wilderness) — verified 2026-08-28" diff --git a/schemas/entry.schema.json b/schemas/entry.schema.json index 1a5b49f..3fdcac0 100644 --- a/schemas/entry.schema.json +++ b/schemas/entry.schema.json @@ -123,6 +123,26 @@ } }, + "mining_selections": { + "type": "object", + "additionalProperties": false, + "properties": { + "fast_xp": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": ["level", "expected_site"], + "additionalProperties": false, + "properties": { + "level": { "type": "integer", "minimum": 1, "maximum": 126 }, + "expected_site": { "type": "string", "minLength": 2 } + } + } + } + } + }, + "mine_sites": { "type": "object", "minProperties": 1, diff --git a/tools/validate.py b/tools/validate.py index c3bacf9..10a5c56 100644 --- a/tools/validate.py +++ b/tools/validate.py @@ -92,6 +92,25 @@ def select_pickaxe(mining_level, tiers): return best +def select_fast_site(mining_level, modes, sites): + """Deterministic fast-xp site selection (mining_selections recompute): + highest ore XP among eligible selectable fast-xp sites whose + mining_level <= level; tie-break lexicographic site id (copper/tin share + an XP+level tie). Returns the site id or None.""" + best_xp, cands = -1, [] + for sid in (modes.get("fast-xp", {}).get("sites", []) or []): + sdef = sites.get(sid) + if not isinstance(sdef, dict) or sdef.get("selectable") is not True: + continue + ml, xp = sdef.get("mining_level"), sdef.get("xp") + if isinstance(ml, int) and isinstance(xp, (int, float)) and ml <= mining_level: + if xp > best_xp: + best_xp, cands = xp, [sid] + elif xp == best_xp: + cands.append(sid) + return sorted(cands)[0] if cands else None + + def mining_model_errors(rel, data): """STRUCTURED mining-model gates (owner guidance 2026-08-28; replaces the earlier phrase-search checks, which could pass on comments alone).""" @@ -230,6 +249,88 @@ def mining_model_errors(rel, data): f"(3600 / (rolls_per_ore x 2.7 s)) — the rate exceeds what " f"the declared click policy can produce") + # --- 4b. GE tool purchase must be executable end to end --- + gev = set(v for v in verbs if v in { + "ge_open", "ge_buy", "ge_offers_done", "ge_collect", "ge_sell"}) + if "ge_buy" in gev: + for must, why in (("ge_open", "open the exchange before placing an offer"), + ("ge_offers_done", "wait one BOUNDED fill window"), + ("ge_collect", "collect the item / refund the gp on timeout")): + if must not in gev: + errors.append( + f"{rel}: ge_buy must be part of an executable GE loop — " + f"missing '{must}' ({why}); a resting offer must never be " + f"retried indefinitely") + elif gev: + if "ge_open" not in gev: + errors.append( + f"{rel}: GE verbs {sorted(gev)} require a ge_open action — " + f"the GE flow must start by opening the exchange") + + # --- 4c. below-15 copper/tin path must be structurally arming-able --- + min_level = (data.get("accounts") or {}).get("min_level") + fx_levels = [] + for sid in (modes.get("fast-xp", {}).get("sites", []) or []): + sdef = sites.get(sid) + if isinstance(sdef, dict) and sdef.get("selectable") is True: + ml = sdef.get("mining_level") + if isinstance(ml, int): + fx_levels.append(ml) + if isinstance(min_level, int) and min_level < 15: + if not fx_levels: + errors.append( + f"{rel}: min_level {min_level} (<15) but fast-xp has no " + f"structured selectable site") + else: + fmin = min(fx_levels) + if fmin > 14: + errors.append( + f"{rel}: fast-xp sites all require Mining >= {fmin} but " + f"the entry claims a copper/tin below-15 path (min_level " + f"{min_level}) — add a structured low-level (<=14) " + f"fallback site or raise min_level") + if fmin > min_level: + errors.append( + f"{rel}: the lowest fast-xp site requires Mining {fmin} " + f"but min_level is {min_level} — a level-{min_level} " + f"account cannot arm a fast-xp run") + + # --- 4d. mining_selections pins (deterministic, recomputed) --- + sel = data.get("mining_selections") + if not isinstance(sel, dict) or not isinstance(sel.get("fast_xp"), list) \ + or not sel["fast_xp"]: + errors.append( + f"{rel}: mining entries must declare mining_selections.fast_xp " + f"(level -> expected_site pins, recomputed by the validator)") + else: + rows = {} + for r in sel["fast_xp"]: + lvl = r.get("level") if isinstance(r, dict) else None + if not isinstance(lvl, int) or lvl < 1: + errors.append( + f"{rel}: mining_selections.fast_xp rows need a positive " + f"integer 'level'") + continue + rows[lvl] = r + if isinstance(min_level, int) and min_level <= 14: + for lvl in (1, 14): + if lvl not in rows: + errors.append( + f"{rel}: mining_selections.fast_xp must pin level " + f"{lvl} (a below-15 account must have a structured, " + f"selectable fast-xp site)") + for lvl, r in rows.items(): + got = select_fast_site(lvl, modes, sites) + if got is None: + errors.append( + f"{rel}: no eligible selectable fast-xp site at level " + f"{lvl} — the low-level (copper/tin) fallback is missing") + elif r.get("expected_site") != got: + errors.append( + f"{rel}: mining_selections level {lvl} expects site " + f"'{r.get('expected_site')}' but the selection rule " + f"picks '{got}'") + # --- 5. fast-xp headline xp rate consistent with the modelled sites --- eco = data.get("economy") xph = eco.get("xp_per_hour") if isinstance(eco, dict) else None @@ -291,6 +392,16 @@ def run_selftest(verbose=False): "modeled_ores_per_hour": 803, "world_hop": False, "selectable": True, "inventory_policy": "drop", "access": "combat 29+", "safety": "gate", "route": "drop in place"}, + "se-varrock-copper": {"ore": "Copper ore", "ore_id": 436, "mining_level": 1, + "xp": 17.5, "rock_count": 2, "respawn_seconds": 5.4, "rolls_per_ore": 1.6, + "modeled_ores_per_hour": 360, "world_hop": False, "selectable": True, + "inventory_policy": "drop", "access": "safe", "safety": "none", + "route": "drop in place"}, + "se-varrock-tin": {"ore": "Tin ore", "ore_id": 438, "mining_level": 1, + "xp": 17.5, "rock_count": 2, "respawn_seconds": 5.4, "rolls_per_ore": 1.6, + "modeled_ores_per_hour": 360, "world_hop": False, "selectable": True, + "inventory_policy": "drop", "access": "safe", "safety": "none", + "route": "drop in place"}, "se-varrock-bank": {"ore": "Iron ore", "ore_id": 440, "mining_level": 15, "xp": 35, "rock_count": 2, "respawn_seconds": 5.4, "rolls_per_ore": 1.33, "modeled_ores_per_hour": 713, "world_hop": False, "selectable": True, @@ -302,14 +413,20 @@ def run_selftest(verbose=False): "selectable": True, "inventory_policy": "bank", "access": "south rocks", "safety": "banked locally", "route": "bank 25s + hop_world"}, } - good = {"pickaxe_tiers": canon, "pickaxe_examples": good_examples, + good = {"accounts": {"min_level": 1}, + "pickaxe_tiers": canon, "pickaxe_examples": good_examples, "mining_modes": {"fast-xp": {"inventory_policy": "drop", - "ore_policy": "iron 15+", - "sites": ["al-kharid-powermine"]}, + "ore_policy": "iron 15+ / copper/tin under 15", + "sites": ["al-kharid-powermine", + "se-varrock-copper", + "se-varrock-tin"]}, "banked-income": {"inventory_policy": "bank", "targets": ["se-varrock-bank", "al-kharid-south-adamantite"], "stop": "gp target"}}, + "mining_selections": {"fast_xp": [ + {"level": 1, "expected_site": "se-varrock-copper"}, + {"level": 14, "expected_site": "se-varrock-copper"}]}, "mine_sites": sites, "actions": [{"id": "a1", "step": "hop-world", "verb": "hop_world", "on_fail": "retry"}], @@ -396,6 +513,33 @@ def run_selftest(verbose=False): check("xp_per_hour 28,000 within 2% of the modelled fast-xp rate 28,105 passes", errs == []) + # below-15 fallback + mining_selections pins (governor bounce 5) + no_fallback = {**good, "mining_modes": { + "fast-xp": {"inventory_policy": "drop", "ore_policy": "iron 15+ only", + "sites": ["al-kharid-powermine"]}, + "banked-income": {"inventory_policy": "bank", "targets": ["se-varrock-bank", + "al-kharid-south-adamantite"], "stop": "gp target"}}} + errs = mining_model_errors("f14.yaml", no_fallback) + check("min_level 1 with iron-only fast-xp sites (no copper/tin fallback) is rejected", + any("below-15" in e for e in errs)) + wrongpin = {**good, "mining_selections": {"fast_xp": [ + {"level": 1, "expected_site": "se-varrock-tin"}, + {"level": 14, "expected_site": "se-varrock-copper"}]}} + errs = mining_model_errors("f15.yaml", wrongpin) + check("mining_selections level-1 expected site mismatching the recomputed selection is rejected", + any("selection rule picks" in e for e in errs)) + no_pin14 = {**good, "mining_selections": {"fast_xp": [ + {"level": 1, "expected_site": "se-varrock-copper"}]}} + errs = mining_model_errors("f16.yaml", no_pin14) + check("mining_selections missing the level-14 pin is rejected when min_level < 15", + any("pin level 14" in e for e in errs)) + ge_no_open = {**good, "actions": [ + {"id": "a1", "step": "buy-pick", "verb": "ge_buy", "on_fail": "retry"}, + {"id": "a2", "step": "hop-world", "verb": "hop_world", "on_fail": "retry"}]} + errs = mining_model_errors("f17.yaml", ge_no_open) + check("ge_buy without ge_open / ge_offers_done / ge_collect is rejected", + any("ge_open" in e for e in errs)) + return errors From a0b03b87c6139b9c75dcd3e6e9ec1a4ad2af7de6 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 29 Aug 2026 03:11:43 -0500 Subject: [PATCH 7/7] =?UTF-8?q?codex:=20mining-f2p=20=E2=80=94=20level-1?= =?UTF-8?q?=20pickaxe=20tie-break:=20Mining=201=20selects+equips=20iron=20?= =?UTF-8?q?(bounce-fix=20attempt=206)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Owner guidance 2026-08-29 for governor review 10476 (PR #11), finding by finding: 1. Explicit tier rank/tie-break for equal Mining gates. pickaxe_tiers declared bronze and iron both at Mining 1 / Attack 1 while the level-1 worked example called bronze canonical and carried it (27 slots) — internally contradictory under the entry's own highest-unlocked acquisition policy. pickaxe_tiers now carries an explicit rank (bronze 1 .. rune 6) and the selection rule resolves equal Mining gates by rank: Mining 1 deterministically selects IRON (7-tick roll, rank 2) over bronze (rank 1), and Attack 1 EQUIPS it (iron attack gate = 1) -> 28 ore slots. Stated at every policy layer (summary, accounts.requires_other, best_practice.equipment, prepare state, economy.supplies, a14 equip-tool gate, pickaxe_tiers head comment). An owned-only bronze run is documented as an operator choice, never the entry's selection rule. 2. Level-1 lifecycle/rate inputs recomputed. The level-1 worked example now uses the equipped iron pickaxe: 28 ore slots per drop load, iron cadence [4200, 6000] ms (midpoint 5.1 s), and the copper/tin rate re-derived from that cadence: 1.6 rolls x 5.1 + 0.5 drop + 0.39 break = 9.05 s/ore -> 398 ores/hr -> ~6,960 XP/hr (pin 6,900), replacing the bronze-midpoint 360 ores/hr / 6,300 XP/hr. Updated in mine_sites (se-varrock-copper / se-varrock-tin), economy comment block, profit_drivers, worktree example, and risk notes; no other rate changes (fast-xp iron 28,000 and banked rates untouched). 3. Recomputed 1-Attack/1-Mining fixture + capacity proof. Schema: pickaxe_tiers gains required rank (1..6); pickaxe_examples gain required expected_slots (28 equipped / 27 carried). Validator: select_pickaxe() ties by rank; every pickaxe_examples row is recomputed including expected_slots, the 1-Attack/41-Mining rune-carry fixture is preserved, and the 1-Attack/1-Mining row is now mandatory (selects iron, equipped, 28). Committed negative fixtures f18-f22: bronze-at-Mining-1, carried-at-Attack-1, wrong slots, missing 1/1 fixture, and a tier missing rank are all rejected (22 selftests pass). Preserved: complete GE tool acquisition loop, structured copper/tin below-15 fallback, fast-xp/banked-income modes, dynamic 27/28 inventory handling, declared banked targets, respawn/world-hop model, and all prior rate gates. version 6 -> 7, updated 2026-08-29. Gate: python3 tools/validate.py -> 2 entries, 0 errors, 1 pre-existing F2P WARN (low-alch-f2p researched, untouched); --selftest -> 22 PASS; git diff --check clean. --- codex/training/mining-f2p.yaml | 84 +++++++++++++++------- schemas/entry.schema.json | 10 +-- tools/validate.py | 124 +++++++++++++++++++++++++++------ 3 files changed, 168 insertions(+), 50 deletions(-) diff --git a/codex/training/mining-f2p.yaml b/codex/training/mining-f2p.yaml index 1c6677a..2d2e7fa 100644 --- a/codex/training/mining-f2p.yaml +++ b/codex/training/mining-f2p.yaml @@ -3,8 +3,8 @@ title: Mining (F2P) domain: training tags: [mining, gathering, f2p, skill-training] status: verified -version: 6 # 6: bounce-fix 5 2026-08-28 — executable GE purchase loop (new a10 ge_open action; bounded fill/no-fill timeout -> cancel/refund/collect -> fail closed, never re-place a resting offer); structured copper/tin below-15 fallback sites (mine_sites se-varrock-copper / se-varrock-tin, level 1, 17.5 xp, safe) pinned in mining_selections (levels 1 & 14) with validator fixtures; a2 restricted to the DECLARED banked-income target rows (iron @ se-varrock-bank, adamantite @ al-kharid-south-adamantite only — silver/coal/gold/mithril no longer advertised) -updated: "2026-08-28" # ISO dates must stay quoted — YAML 1.1 parses bare dates to datetime.date +version: 7 # 7: bounce-fix 6 2026-08-29 (owner guidance for governor 10476) — explicit tier-rank tie-break (rank 1..6; equal Mining gates resolve to the higher rank): Mining 1 now selects IRON (7-tick roll), Attack 1 EQUIPS it (attack gate 1) -> 28 ore slots; level-1 worked lifecycle/rates recomputed (iron cadence midpoint 5.1 s -> 398 ores/hr -> pin 6,900 XP/hr); pickaxe_examples gain expected_slots (28 equipped / 27 carried) + the recomputed 1-Attack/1-Mining fixture (iron, equipped, 28) — 1-Attack/41-Mining rune-carry fixture preserved; validator + schema enforce rank and slots. # 6: bounce-fix 5 2026-08-28 — executable GE purchase loop (new a10 ge_open action; bounded fill/no-fill timeout -> cancel/refund/collect -> fail closed, never re-place a resting offer); structured copper/tin below-15 fallback sites (mine_sites se-varrock-copper / se-varrock-tin, level 1, 17.5 xp, safe) pinned in mining_selections (levels 1 & 14) with validator fixtures; a2 restricted to the DECLARED banked-income target rows (iron @ se-varrock-bank, adamantite @ al-kharid-south-adamantite only — silver/coal/gold/mithril no longer advertised) +updated: "2026-08-29" # ISO dates must stay quoted — YAML 1.1 parses bare dates to datetime.date summary: > F2P mining with TWO explicit run modes, chosen at session start and @@ -31,13 +31,18 @@ summary: > ATTACK level permits (rune equip = 40 Attack), otherwise CARRY the same selected tool (a 1-Attack/41-Mining account carries and uses a rune pickaxe — pinned in pickaxe_examples and recomputed by the validator). + TIE-BREAK: tiers with equal Mining gates resolve by explicit rank + (pickaxe_tiers rank 1..6) — bronze and iron share the Mining-1 gate, so + Mining 1 selects IRON (7-tick roll), and Attack 1 equips it (attack + gate 1): a 1-Attack/1-Mining account mines copper/tin with an EQUIPPED + iron pickaxe and 28 ore slots (fixture in pickaxe_examples). accounts: membership: f2p # whole ore chain verified F2P via the wiki guide min_level: 1 # clay/copper/tin mineable at level 1 requires_quest: [] requires_other: - - "Pickaxe: two SEPARATE gates per tier (pickaxe_tiers table): MINING level decides which pickaxe can be USED (bronze 1, iron 1, steel 6, mithril 21, adamant 31, rune 41); ATTACK level decides ONLY whether it can be EQUIPPED (steel 5, mithril 20, adamant 30, rune 40). Select by Mining, equip only when Attack permits, otherwise CARRY the same selected tool — a 1-Attack/41-Mining account selects, carries, and uses a rune pickaxe (pickaxe_examples)" + - "Pickaxe: two SEPARATE gates per tier (pickaxe_tiers table): MINING level decides which pickaxe can be USED (bronze 1, iron 1, steel 6, mithril 21, adamant 31, rune 41); ATTACK level decides ONLY whether it can be EQUIPPED (steel 5, mithril 20, adamant 30, rune 40). Select by Mining, equip only when Attack permits, otherwise CARRY the same selected tool — a 1-Attack/41-Mining account selects, carries, and uses a rune pickaxe. Equal Mining gates resolve by explicit tier rank (rank 1..6): at Mining 1 the tie-break selects IRON over bronze, and Attack 1 EQUIPS it (attack gate 1) — pickaxe_examples pins 1-Attack/1-Mining (iron, equipped, 28 ore slots)" - "Run mode chosen at session start: fast-xp (powermine, drop every load) or banked-income (deterministic target mine_sites row, bank every load) — mining_modes table" - "Bank route from the chosen mine (fast-xp drops instead when no bank leg is wanted)" - "Below-15 fast-xp (Mining 1-14): copper/tin (17.5 xp) at the structured East Varrock fallback rows (mine_sites se-varrock-copper / se-varrock-tin — no aggressive NPCs, safe at any combat level); the deterministic selection is pinned in mining_selections (levels 1 and 14) and recomputed by the validator" @@ -47,7 +52,7 @@ accounts: best_practice: equipment: - item: "pickaxe: highest tier usable at the account's MINING level (pickaxe_tiers: bronze 1, iron 1, steel 6, mithril 21, adamant 31, rune 41)" - why: "each tier is faster — the roll interval drops from 8 ticks (bronze) to 3 ticks (rune), and the guide recommends always using the highest tier available. Two SEPARATE gates: the Mining level decides WHICH pickaxe can be USED, the Attack level decides ONLY whether it can be EQUIPPED (steel 5, mithril 20, adamant 30, rune 40). Equip only when the Attack requirement is met, otherwise carry the same selected tool — a 1-Attack/41-Mining account selects, carries, and uses a rune pickaxe" + why: "each tier is faster — the roll interval drops from 8 ticks (bronze) to 3 ticks (rune), and the guide recommends always using the highest tier available. Two SEPARATE gates: the Mining level decides WHICH pickaxe can be USED, the Attack level decides ONLY whether it can be EQUIPPED (steel 5, mithril 20, adamant 30, rune 40). Equip only when the Attack requirement is met, otherwise carry the same selected tool — a 1-Attack/41-Mining account selects, carries, and uses a rune pickaxe. TIE-BREAK on equal Mining gates: explicit rank (pickaxe_tiers rank 1..6) — bronze and iron both open at Mining 1, so Mining 1 selects IRON (rank 2) and Attack 1 EQUIPS it (attack gate 1, 28 ore slots), never bronze (rank 1)" source: "OSRS wiki — Free-to-play Mining training (Pickaxes: tier table with tick intervals); OSRS wiki — Rune pickaxe (41 Mining to use, 40 Attack to equip) and Steel/Mithril/Adamant pickaxe pages (use vs equip gates)" techniques: - name: "mode contract: fast-xp vs banked-income (chosen at session start, validated against mine_sites, never silently highest-available)" @@ -78,54 +83,79 @@ best_practice: why: "Account: Mining 70, Attack 40, combat 60. Target: 500,000 gp banked or a 2 h session cap. (1) prepare: mode banked-income; target row = al-kharid-south-adamantite (mine_sites: ore level 70 <= Mining 70 OK; access/route validated; inventory policy bank) — the row id is logged to activity state; pickaxe by MINING level -> rune (41 <= 70); equip gate Attack 40 >= 40 -> EQUIP (28 ore slots); tool owned, withdraw from bank (a9). (2) travel: walk to the Al Kharid south adamantite spot. (3) mining rotations: click a fresh adamantite rock (rune roll 1.8 s, delay [1800, 3600] ms) -> wait_rock: ore gained -> mine; when BOTH rocks are depleted -> hop: hop_world (a15) to a fresh F2P world (world_hop row) and continue — this is where the wiki's 120 ores/hr comes from. (4) first full-inventory boundary: 28 ores -> bank_run: walk to the Al Kharid bank (a3), deposit all 28 (a4; 28 x 584 = 16,352 gp logged), walk back (a5) -> mine. (5) first scheduled break: after 8-14 rotations -> break_state: take_break 60-180 s (independent of the bank trips). (6) resume: reposition (a17) -> mine; complete when the banked value >= 500,000 gp or the session cap hits" source: "derived from this entry's policy model (state_machine prepare/travel/mine/wait_rock/hop/bank_run/break_state/complete + actions + mine_sites.al-kharid-south-adamantite), synthesized 2026-08-28" - name: "worked example — level-1 fast-xp copper/tin session (preparation -> first full inventory -> first break -> resume)" - why: "Account: Mining 1, Attack 1, combat 3. (1) prepare: mode fast-xp; below-15 selection pinned at level 1 (mining_selections) -> se-varrock-copper / se-varrock-tin; pickaxe by MINING -> the canonical selection at Mining 1 (bronze; iron shares the Mining-1 gate and can be substituted once owned); equip gate Attack 1 -> no equip, CARRY; the tool is owned, so acquire = a9 withdraw-tool (bank_inventory). (2) travel: walk to the East Varrock copper/tin rocks (safe — no aggressive NPCs) and position at the cluster. (3) mining rotations: click a fresh copper or tin rock (a2, bronze cadence [4800, 6600] ms) -> wait_rock: ore gained after ~1.6 rolls (success model) -> mine; both ores are 17.5 xp each. (4) first full-inventory boundary: 27 ores -> drop_run: a6 drops ONE slot per execution (right_click Drop, [200, 800] ms), looping until count_item ore == 0 -> mine. (5) first scheduled break: after the 8th-14th rotation the break counter trips -> break_state: take_break 60-180 s (a16). (6) resume: reposition at the cluster (a17) -> mine; complete on the XP/level target. Modeled policy rate: ~360 ores/hr = 6,300 XP/hr at the bronze cadence midpoint (economy)." - source: "derived from this entry's policy model (mining_selections levels 1/14 + mine_sites se-varrock-copper / se-varrock-tin + state_machine + actions a9/a2/a6/a16/a17), synthesized 2026-08-28" + why: "Account: Mining 1, Attack 1, combat 3. (1) prepare: mode fast-xp; below-15 selection pinned at level 1 (mining_selections) -> se-varrock-copper / se-varrock-tin; pickaxe by MINING with the rank TIE-BREAK: bronze and iron both meet the Mining-1 gate, and rank 2 (iron, 7-tick roll) beats rank 1 (bronze, 8-tick) -> IRON is selected; equip gate Attack 1 >= iron's attack gate 1 -> EQUIP the iron pickaxe (28 ore slots); the tool is owned, so acquire = a9 withdraw-tool (bank_inventory). (2) travel: walk to the East Varrock copper/tin rocks (safe — no aggressive NPCs) and position at the cluster. (3) mining rotations: click a fresh copper or tin rock (a2, iron cadence [4200, 6000] ms) -> wait_rock: ore gained after ~1.6 rolls (success model) -> mine; both ores are 17.5 xp each. (4) first full-inventory boundary: 28 ores -> drop_run: a6 drops ONE slot per execution (right_click Drop, [200, 800] ms), looping until count_item ore == 0 -> mine. (5) first scheduled break: after the 8th-14th rotation the break counter trips -> break_state: take_break 60-180 s (a16). (6) resume: reposition at the cluster (a17) -> mine; complete on the XP/level target. Modeled policy rate: ~398 ores/hr = ~6,960 XP/hr at the iron cadence midpoint 5.1 s (pin 6,900; economy)." + source: "derived from this entry's policy model (rank tie-break in pickaxe_tiers + pickaxe_examples 1-Attack/1-Mining + mining_selections levels 1/14 + mine_sites se-varrock-copper / se-varrock-tin + state_machine + actions a9/a2/a6/a16/a17), synthesized 2026-08-29" pickaxe_tiers: + # rank = explicit tier order (1..6) used as the SELECTION tie-break: + # among tiers whose mining_level gate the account meets, the highest + # rank wins. Bronze and iron share the Mining 1 gate, so at Mining 1 the + # rule deterministically selects IRON (rank 2, 7-tick roll) — never + # bronze. (An owned-only bronze run is an operator choice, not the + # entry's selection rule.) attack_level remains the equip-ONLY gate: + # iron's attack gate is 1, so a 1-Attack/1-Mining account EQUIPS it. bronze-pickaxe: mining_level: 1 # usability gate: Mining level needed to USE this pickaxe attack_level: 1 # equip gate: Attack level needed only to EQUIP it roll_ticks: 8 # ore roll interval (ticks x 0.6 s = 4.8 s) + rank: 1 iron-pickaxe: mining_level: 1 attack_level: 1 roll_ticks: 7 + rank: 2 steel-pickaxe: mining_level: 6 attack_level: 5 roll_ticks: 6 + rank: 3 mithril-pickaxe: mining_level: 21 attack_level: 20 roll_ticks: 5 + rank: 4 adamant-pickaxe: mining_level: 31 attack_level: 30 roll_ticks: 4 + rank: 5 rune-pickaxe: mining_level: 41 attack_level: 40 roll_ticks: 3 + rank: 6 # Structured proof of the two-gate rule (validator recomputes each row from -# pickaxe_tiers and compares): select by MINING, equip only when ATTACK -# permits, carry otherwise. The 1-Attack/41-Mining account is owner-mandated. +# pickaxe_tiers and compares): select by MINING (tie-break: highest rank), +# equip only when ATTACK permits, carry otherwise. expected_slots = 28 +# (equipped pickaxe leaves 28 ore slots) or 27 (carried pickaxe occupies +# one slot). The 1-Attack/41-Mining account is owner-mandated; the +# 1-Attack/1-Mining row proves the level-1 tie-break (iron, equipped, 28). pickaxe_examples: + - account: "1-Attack/1-Mining" + attack: 1 + mining: 1 + expected_tier: iron-pickaxe + expected_equipped: true # 1 Mining: bronze/iron both eligible -> rank tie-break -> IRON; Attack 1 >= 1 -> EQUIP (28 ore slots) + expected_slots: 28 - account: "1-Attack/41-Mining" attack: 1 mining: 41 expected_tier: rune-pickaxe - expected_equipped: false # 41 Mining selects rune; Attack 1 < 40 -> CARRY + expected_equipped: false # 41 Mining selects rune; Attack 1 < 40 -> CARRY (27 ore slots) + expected_slots: 27 - account: "40-Attack/70-Mining" attack: 40 mining: 70 expected_tier: rune-pickaxe - expected_equipped: true # 70 Mining selects rune; Attack 40 >= 40 -> EQUIP + expected_equipped: true # 70 Mining selects rune; Attack 40 >= 40 -> EQUIP (28 ore slots) + expected_slots: 28 - account: "5-Attack/30-Mining" attack: 5 mining: 30 expected_tier: mithril-pickaxe - expected_equipped: false # 30 Mining selects mithril (21); Attack 5 < 20 -> CARRY + expected_equipped: false # 30 Mining selects mithril (21); Attack 5 < 20 -> CARRY (27 ore slots) + expected_slots: 27 # Deterministic mode-selection proof (governor bounce 5, 2026-08-28): the # validator recomputes every row from mining_modes + mine_sites (eligible = @@ -196,7 +226,7 @@ mine_sites: rock_count: 2 respawn_seconds: 5.4 rolls_per_ore: 1.6 # below-15 success model (lower than iron 61+ = 1.33) - modeled_ores_per_hour: 360 # bronze cadence midpoint 5.7 s: 1.6 x 5.7 + 0.5 + 0.39 = 10.01 s/ore (economy) + modeled_ores_per_hour: 398 # iron cadence midpoint 5.1 s (tie-break select: iron at Mining 1): 1.6 x 5.1 + 0.5 + 0.39 = 9.05 s/ore -> 398 (economy) world_hop: false selectable: true inventory_policy: drop @@ -211,7 +241,7 @@ mine_sites: rock_count: 2 respawn_seconds: 5.4 rolls_per_ore: 1.6 - modeled_ores_per_hour: 360 + modeled_ores_per_hour: 398 # iron cadence midpoint 5.1 s (see se-varrock-copper) world_hop: false selectable: true inventory_policy: drop @@ -291,10 +321,12 @@ economy: # banked adamantite @ Al Kharid south (hop): 120 ores/hr (sourced # measured baseline) x 584 = 70,080 gp/hr; no-hop ceiling ~30 ores/hr # = 17,520 gp/hr — the entry models ONLY the hop rate. - # copper/tin path (Mining 1-14, 17.5 xp): cadence midpoint 5.7 s - # (bronze [4800, 6600]) -> 1.6 rolls x 5.7 + 0.5 drop + 0.39 break = - # 10.01 s/ore -> 360 ores/hr -> 6,300 XP/hr (mine_sites - # se-varrock-copper / se-varrock-tin). + # copper/tin path (Mining 1-14, 17.5 xp): the rank tie-break at Mining 1 + # selects IRON (bronze and iron share the Mining-1 gate; rank 2 beats + # rank 1) and Attack 1 EQUIPS it (attack gate 1) -> 28 ore slots, iron + # cadence midpoint 5.1 s (iron [4200, 6000]) -> 1.6 rolls x 5.1 + 0.5 + # drop + 0.39 break = 9.05 s/ore -> 398 ores/hr -> ~6,960 XP/hr -> pin + # 6,900 (mine_sites se-varrock-copper / se-varrock-tin). # EXTERNAL BENCHMARK (NOT the modeled rate): the wiki's 50,000 XP/hr # (41+) / 52,500 XP/hr (61+) iron powermine figures assume a continuous # 3-tick rotation with zero delay/drop/break overhead; the configured @@ -310,14 +342,14 @@ economy: gp_per_hour_high: 70080 # top modeled banked-income rate: adamantite WITH world hopping (120 ores/hr x 584); iron @ SE Varrock 49,900; no-hop adamantite 17,520 documented in profit_drivers xp_per_hour: 28000 # MODELLED fast-xp policy rate (rune 61+ drop, success model + drop loop + breaks) — NOT the 52,500 wiki benchmark supplies: - - item: "pickaxe — best tier selectable by MINING level (pickaxe_tiers: bronze 1, iron 1, steel 6, mithril 21, adamant 31, rune 41); equip ONLY when the ATTACK level permits (steel 5, mithril 20, adamant 30, rune 40), otherwise carry the same selected tool (a 1-Attack/41-Mining account carries and uses a rune pickaxe)" + - item: "pickaxe — best tier selectable by MINING level (pickaxe_tiers: bronze 1, iron 1, steel 6, mithril 21, adamant 31, rune 41; equal Mining gates tie-break by rank, so Mining 1 selects iron); equip ONLY when the ATTACK level permits (steel 5, mithril 20, adamant 30, rune 40), otherwise carry the same selected tool (a 1-Attack/41-Mining account carries and uses a rune pickaxe; a 1-Attack/1-Mining account equips its iron pickaxe — attack gate 1)" qty_per_cycle: 1 source: bank profit_drivers: - "F2P ore chain (wiki guide): clay 1/5xp, copper 1/17.5, tin 1/17.5, iron 15/35, silver 20/40, coal 30/50, gold 40/65, mithril 55/80, adamantite 70/95, runite 85/125" - "Sell prices 2026-08-27 (low edge): iron 70, coal 151, silver 73, gold 154, mithril 159, adamantite 584, runite 10,265" - "fast-xp: powermine iron 15+ and drop every load — XP is the product, realized gp 0; modeled 803 ores/hr -> 28,105 XP/hr (pin 28,000) at 61+ rune" - - "fast-xp 1-14 (copper/tin 17.5 xp): modeled ~360 ores/hr -> 6,300 XP/hr at the bronze cadence midpoint (economy comment) — structured rows se-varrock-copper / se-varrock-tin, pinned at levels 1 and 14 in mining_selections" + - "fast-xp 1-14 (copper/tin 17.5 xp): modeled ~398 ores/hr -> ~6,960 XP/hr at the iron cadence midpoint (rank tie-break at Mining 1 selects iron, equipped at Attack 1; pin 6,900) — structured rows se-varrock-copper / se-varrock-tin, pinned at levels 1 and 14 in mining_selections" - "banked-income iron @ SE Varrock: modeled 713 ores/hr -> 49,910 gp/hr (pin 49,900); deterministic target row (mine_sites.se-varrock-bank)" - "banked-income adamantite @ Al Kharid south: respawn-BOUND site (2 rocks, 4-min respawn) — modeled 120 ores/hr ONLY with world hopping (hop_world) = 70,080 gp/hr; without hopping the ceiling is ~30 ores/hr = 17,520 gp/hr (wiki F2P adamantite money guide)" - "runite is NOT a selectable target in this entry — F2P runite sits in the Wilderness (PK risk, ~12-min respawn); the mine_sites row is selectable: false" @@ -341,10 +373,14 @@ state_machine: requires the hop policy), inventory policy == bank; the row id is logged to activity state. 3) Pickaxe: select the highest pickaxe_tiers row whose mining_level the account meets (rune is - usable at 41 Mining and equipable at 40 Attack). 4) Equip ONLY when + usable at 41 Mining and equipable at 40 Attack); equal Mining + gates resolve by explicit tier rank (rank 1..6) — bronze and iron + both open at Mining 1, so Mining 1 selects IRON. 4) Equip ONLY when level_is Attack >= the row's attack_level; otherwise carry the same selected tool (a 1-Attack/41-Mining account selects and CARRIES a - rune pickaxe). 5) Acquire the tool if missing (a8-a13: check / + rune pickaxe; a 1-Attack/1-Mining account selects and EQUIPS an + iron pickaxe — attack gate 1 — with 28 ore slots). 5) Acquire the + tool if missing (a8-a13: check / withdraw-owned / open GE / place offer / bounded wait+collect) and build the initial inventory: pickaxe (equipped or carried) + empty ore space; no food (all routes gate-safe, incl. the copper/tin @@ -656,7 +692,7 @@ actions: target: "the selected pickaxe tier in the inventory" params: option: "Wield" - gate: "RUN ONLY IF level_is Attack >= the tier's attack_level (steel 5, mithril 20, adamant 30, rune 40) — otherwise SKIP and carry the same selected tool in the inventory; mining from the inventory works at any level (a 1-Attack/41-Mining account mines with a carried rune pickaxe)" + gate: "RUN ONLY IF level_is Attack >= the tier's attack_level (steel 5, mithril 20, adamant 30, rune 40) — otherwise SKIP and carry the same selected tool in the inventory; mining from the inventory works at any level (a 1-Attack/41-Mining account mines with a carried rune pickaxe; a 1-Attack/1-Mining account DOES equip its iron pickaxe — attack gate 1 — giving 28 ore slots)" on_fail: skip anti_ban: delay: [600, 1400] @@ -713,9 +749,9 @@ risk: - "Mining is a staple low-risk method: long, repetitive, low-input behaviour that matches normal player sessions — the guide itself recommends hours of drop-mining." - "The powermine rotation is the detection vector if executed perfectly for hours — keep the a2 tier-aware cadence (wait_rock outcome wait + roll_table pause; anchor rune 1.8-3.6 s, never below the equipped tier's full roll) and take the break schedule." - "Al Kharid mine has aggressive level-14 scorpions (they attack players at combat level <= 28 — wiki Aggressiveness). The entry HARD-gates Al Kharid to combat 29+; sub-29 accounts run fast-xp at the South-east Varrock DROP site (no aggressive NPCs, same mode). No food is part of either route; the survive state (retreat) is a defensive fallback only." - - "Below-15 fast-xp stays at the safe East Varrock copper/tin rocks (se-varrock-copper / se-varrock-tin — no aggressive NPCs), so no food or combat gate applies; the mode is unchanged (drop every load)." + - "Below-15 fast-xp stays at the safe East Varrock copper/tin rocks (se-varrock-copper / se-varrock-tin — no aggressive NPCs), so no food or combat gate applies; the mode is unchanged (drop every load). At Mining 1 the rank tie-break selects the iron pickaxe (equal Mining gate, higher rank) and Attack 1 equips it." - "World hopping (adamantite row only) is pace-sensitive: hop at the depletion trigger, not on a timer, and back off after 8 fruitless hops. The modelled 120 ores/hr is the wiki's hopping baseline — never claim it without the hop policy." - - "Bottom line on rates: 28,000 XP/hr (fast-xp iron 15+) and 6,300 XP/hr (fast-xp copper/tin below 15) are this entry's modelled fast-xp policy rates; 70,080 gp/hr (adamantite w/ hop) is the banked-income modelled rate — all derived in economy; the wiki's 52,500 XP/hr is a labeled external benchmark, not an achieved rate." + - "Bottom line on rates: 28,000 XP/hr (fast-xp iron 15+) and 6,900 XP/hr (fast-xp copper/tin below 15) are this entry's modelled fast-xp policy rates; 70,080 gp/hr (adamantite w/ hop) is the banked-income modelled rate — all derived in economy; the wiki's 52,500 XP/hr is a labeled external benchmark, not an achieved rate." sources: - "OSRS wiki — Active/passive aggressive monsters: Aggressiveness (a monster attacks players whose combat level is <= 2x its level; a level-14 scorpion is safe from combat level 29) — verified 2026-08-27" diff --git a/schemas/entry.schema.json b/schemas/entry.schema.json index 3fdcac0..d19b2d3 100644 --- a/schemas/entry.schema.json +++ b/schemas/entry.schema.json @@ -68,12 +68,13 @@ "propertyNames": { "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$" }, "additionalProperties": { "type": "object", - "required": ["mining_level", "attack_level", "roll_ticks"], + "required": ["mining_level", "attack_level", "roll_ticks", "rank"], "additionalProperties": false, "properties": { "mining_level": { "type": "integer", "minimum": 1, "maximum": 126 }, "attack_level": { "type": "integer", "minimum": 1, "maximum": 126 }, - "roll_ticks": { "type": "integer", "minimum": 1 } + "roll_ticks": { "type": "integer", "minimum": 1 }, + "rank": { "type": "integer", "minimum": 1, "maximum": 6 } } } }, @@ -83,14 +84,15 @@ "minItems": 1, "items": { "type": "object", - "required": ["account", "attack", "mining", "expected_tier", "expected_equipped"], + "required": ["account", "attack", "mining", "expected_tier", "expected_equipped", "expected_slots"], "additionalProperties": false, "properties": { "account": { "type": "string", "minLength": 2 }, "attack": { "type": "integer", "minimum": 1, "maximum": 126 }, "mining": { "type": "integer", "minimum": 1, "maximum": 126 }, "expected_tier": { "type": "string", "minLength": 2 }, - "expected_equipped": { "type": "boolean" } + "expected_equipped": { "type": "boolean" }, + "expected_slots": { "type": "integer", "minimum": 1, "maximum": 28 } } } }, diff --git a/tools/validate.py b/tools/validate.py index 10a5c56..a450220 100644 --- a/tools/validate.py +++ b/tools/validate.py @@ -15,11 +15,15 @@ Runs: checks on the declared `pickaxe_tiers` / `pickaxe_examples` / `mining_modes` / `mine_sites` blocks (owner guidance 2026-08-28): * pickaxe tier values must match the canonical F2P table exactly - (Mining = usability gate, Attack = equip gate only, roll ticks); + (Mining = usability gate, Attack = equip gate only, roll ticks, + and the explicit rank tie-break for equal Mining gates — Mining 1 + selects iron over bronze, equipped at Attack 1); * every pickaxe_examples row is recomputed (select highest tier - whose mining_level <= mining; equipped = attack >= that tier's - attack_level) and must match the declared expectation — the - pinned 1-Attack/41-Mining account must select and CARRY rune; + whose mining_level <= mining, tie-break by rank; equipped = attack + >= that tier's attack_level; expected_slots = 28 equipped / 27 + carried) and must match the declared expectation — the pinned + 1-Attack/41-Mining account must select and CARRY rune and the + 1-Attack/1-Mining account must select and EQUIP iron; * both run modes must be declared with the correct inventory policy (fast-xp = drop, banked-income = bank) and only reference declared, selectable sites whose policy matches; @@ -56,14 +60,17 @@ STALE_DAYS = 30 # Canonical F2P pickaxe table (OSRS wiki pickaxe pages, verified 2026-08-28): # mining_level = which pickaxe can be USED; attack_level = only whether it can -# be EQUIPPED; roll_ticks = ore roll interval (ticks x 0.6 s). +# be EQUIPPED; roll_ticks = ore roll interval (ticks x 0.6 s); +# rank = explicit tier order used as the SELECTION tie-break for equal +# Mining gates (bronze and iron both open at Mining 1 -> Mining 1 = iron, +# per the owner decision on governor review 10476). CANONICAL_PICKAXES = { - "bronze-pickaxe": {"mining_level": 1, "attack_level": 1, "roll_ticks": 8}, - "iron-pickaxe": {"mining_level": 1, "attack_level": 1, "roll_ticks": 7}, - "steel-pickaxe": {"mining_level": 6, "attack_level": 5, "roll_ticks": 6}, - "mithril-pickaxe": {"mining_level": 21, "attack_level": 20, "roll_ticks": 5}, - "adamant-pickaxe": {"mining_level": 31, "attack_level": 30, "roll_ticks": 4}, - "rune-pickaxe": {"mining_level": 41, "attack_level": 40, "roll_ticks": 3}, + "bronze-pickaxe": {"mining_level": 1, "attack_level": 1, "roll_ticks": 8, "rank": 1}, + "iron-pickaxe": {"mining_level": 1, "attack_level": 1, "roll_ticks": 7, "rank": 2}, + "steel-pickaxe": {"mining_level": 6, "attack_level": 5, "roll_ticks": 6, "rank": 3}, + "mithril-pickaxe": {"mining_level": 21, "attack_level": 20, "roll_ticks": 5, "rank": 4}, + "adamant-pickaxe": {"mining_level": 31, "attack_level": 30, "roll_ticks": 4, "rank": 5}, + "rune-pickaxe": {"mining_level": 41, "attack_level": 40, "roll_ticks": 3, "rank": 6}, } # a2 anti_ban delay midpoint [1800, 3600] ms — the paced roll-attempt cadence # the rate model derives from (rune anchor; see mining-f2p.yaml economy). @@ -81,14 +88,21 @@ def load_entries(): def select_pickaxe(mining_level, tiers): - """Highest tier whose mining_level gate the account meets (None if none).""" - best, best_lvl = None, -1 + """Highest eligible tier by the two-gate rule: among tiers whose + mining_level gate the account meets, the highest RANK wins (rank is + the explicit tie-break for equal Mining gates — bronze and iron both + open at Mining 1, so Mining 1 deterministically selects iron).""" + best, best_key = None, None for tid, tdef in (tiers or {}).items(): if not isinstance(tdef, dict): continue gate = tdef.get("mining_level") - if isinstance(gate, int) and gate <= mining_level and gate > best_lvl: - best, best_lvl = tid, gate + if not isinstance(gate, int) or gate > mining_level: + continue + rank = tdef.get("rank") + key = (rank if isinstance(rank, int) else gate, gate) + if best_key is None or key > best_key: + best, best_key = tid, key return best @@ -146,13 +160,15 @@ def mining_model_errors(rel, data): errors.append(f"{rel}: pickaxe_examples must declare at least one account " f"example for the two-gate selection proof") else: - has_141 = False + has_141 = has_11 = False for ex in examples: if not isinstance(ex, dict): continue atk, mng = ex.get("attack"), ex.get("mining") if atk == 1 and mng == 41: has_141 = True + if atk == 1 and mng == 1: + has_11 = True chosen = select_pickaxe(mng, tiers) if chosen is None: errors.append(f"{rel}: example '{ex.get('account')}' (Mining {mng}) " @@ -170,11 +186,30 @@ def mining_model_errors(rel, data): f"{rel}: example '{ex.get('account')}' (Attack {atk}/Mining " f"{mng}) expects equipped={ex.get('expected_equipped')} but " f"the Attack-equip gate on '{chosen}' says equipped={should_equip}") + # capacity proof: equipped pickaxe -> 28 ore slots, carried -> 27 + want_slots = 28 if should_equip else 27 + if ex.get("expected_slots") is None: + errors.append( + f"{rel}: example '{ex.get('account')}' must declare " + f"expected_slots ({want_slots} for tier '{chosen}' " + f"equipped={should_equip})") + elif ex.get("expected_slots") != want_slots: + errors.append( + f"{rel}: example '{ex.get('account')}' expects " + f"expected_slots={ex.get('expected_slots')} but the " + f"declared pickaxe '{chosen}' equipped={should_equip} " + f"gives {want_slots} ore slots (28 equipped / 27 carried)") if not has_141: errors.append( f"{rel}: pickaxe_examples must include the owner-mandated " f"1-Attack/41-Mining account (selects, carries, and uses a rune " f"pickaxe — equipped=false)") + if not has_11: + errors.append( + f"{rel}: pickaxe_examples must include the 1-Attack/1-Mining " + f"account (owner decision on governor review 10476: bronze and " + f"iron share the Mining-1 gate, the rank tie-break selects " + f"IRON, and Attack 1 equips it — equipped=true, 28 ore slots)") # --- 3. run-mode contract --- modes = data.get("mining_modes") @@ -377,14 +412,20 @@ def run_selftest(verbose=False): if not passed: errors.append(f"selftest: {name}") - canon = dict(CANONICAL_PICKAXES) + canon = {k: dict(v) for k, v in CANONICAL_PICKAXES.items()} # deep copy — fixtures below must never mutate the module table good_examples = [ + {"account": "1-Attack/1-Mining", "attack": 1, "mining": 1, + "expected_tier": "iron-pickaxe", "expected_equipped": True, + "expected_slots": 28}, {"account": "1-Attack/41-Mining", "attack": 1, "mining": 41, - "expected_tier": "rune-pickaxe", "expected_equipped": False}, + "expected_tier": "rune-pickaxe", "expected_equipped": False, + "expected_slots": 27}, {"account": "40-Attack/70-Mining", "attack": 40, "mining": 70, - "expected_tier": "rune-pickaxe", "expected_equipped": True}, + "expected_tier": "rune-pickaxe", "expected_equipped": True, + "expected_slots": 28}, {"account": "5-Attack/30-Mining", "attack": 5, "mining": 30, - "expected_tier": "mithril-pickaxe", "expected_equipped": False}, + "expected_tier": "mithril-pickaxe", "expected_equipped": False, + "expected_slots": 27}, ] sites = { "al-kharid-powermine": {"ore": "Iron ore", "ore_id": 440, "mining_level": 15, @@ -443,16 +484,55 @@ def run_selftest(verbose=False): # 1-Attack/41-Mining must carry rune — declared expectation is enforced bad_141 = {"account": "1-Attack/41-Mining", "attack": 1, "mining": 41, - "expected_tier": "rune-pickaxe", "expected_equipped": True} + "expected_tier": "rune-pickaxe", "expected_equipped": True, + "expected_slots": 27} errs = mining_model_errors("bad2.yaml", {**good, "pickaxe_examples": [bad_141]}) check("1-Attack/41-Mining example expecting equipped=True is rejected (equip gate: Attack 1 < 40)", any("equipped=True" in e and "equipped=False" in e for e in errs)) errs = mining_model_errors("bad3.yaml", {**good, "pickaxe_examples": [ {"account": "40-Attack/70-Mining", "attack": 40, "mining": 70, - "expected_tier": "rune-pickaxe", "expected_equipped": True}]}) + "expected_tier": "rune-pickaxe", "expected_equipped": True, + "expected_slots": 28}]}) check("missing the 1-Attack/41-Mining example is rejected (owner-mandated)", any("1-Attack/41-Mining" in e for e in errs)) + # level-1 tie-break (governor review 10476, owner decision 2026-08-29): + # Mining 1 must select IRON (rank tie-break over bronze) and EQUIP it + # (attack gate 1) with 28 ore slots — the old bronze-canonical model is + # rejected on three independent axes + tie_bronze = {"account": "1-Attack/1-Mining", "attack": 1, "mining": 1, + "expected_tier": "bronze-pickaxe", "expected_equipped": True, + "expected_slots": 28} + errs = mining_model_errors("f18.yaml", {**good, "pickaxe_examples": + [x for x in good_examples if x["mining"] != 1] + [tie_bronze]}) + check("Mining 1 expecting the bronze pickaxe is rejected (rank tie-break selects iron)", + any("'bronze-pickaxe'" in e and "selects 'iron-pickaxe'" in e for e in errs)) + tie_carry = {"account": "1-Attack/1-Mining", "attack": 1, "mining": 1, + "expected_tier": "iron-pickaxe", "expected_equipped": False, + "expected_slots": 27} + errs = mining_model_errors("f19.yaml", {**good, "pickaxe_examples": + [x for x in good_examples if x["mining"] != 1] + [tie_carry]}) + check("1-Attack/1-Mining expecting a CARRIED iron pickaxe is rejected (Attack 1 >= iron's attack gate 1 -> equipped)", + any("equipped=True" in e and "equipped=False" in e for e in errs) or + any("gives 28 ore slots" in e for e in errs)) + tie_slots = {"account": "1-Attack/1-Mining", "attack": 1, "mining": 1, + "expected_tier": "iron-pickaxe", "expected_equipped": True, + "expected_slots": 27} + errs = mining_model_errors("f20.yaml", {**good, "pickaxe_examples": + [x for x in good_examples if x["mining"] != 1] + [tie_slots]}) + check("1-Attack/1-Mining with 27 slots while equipped is rejected (equipped iron = 28 ore slots)", + any("28 equipped / 27 carried" in e for e in errs)) + no_11 = {**good, "pickaxe_examples": + [x for x in good_examples if not (x["attack"] == 1 and x["mining"] == 1)]} + errs = mining_model_errors("f21.yaml", no_11) + check("missing the 1-Attack/1-Mining fixture is rejected (owner-mandated tie-break proof)", + any("1-Attack/1-Mining" in e for e in errs)) + bad_tiers2 = {k: dict(v) for k, v in canon.items()} + del bad_tiers2["iron-pickaxe"]["rank"] + errs = mining_model_errors("f22.yaml", {**good, "pickaxe_tiers": bad_tiers2}) + check("a pickaxe tier missing the rank field is rejected (tie-break must be explicit)", + any("iron-pickaxe" in e and "rank" in e for e in errs)) + # mode contract errs = mining_model_errors("bad4.yaml", {**good, "mining_modes": { "fast-xp": {"inventory_policy": "bank",