codex: mining-f2p — level-1 pickaxe tie-break: Mining 1 selects+equips iron (bounce-fix attempt 6)

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.
This commit is contained in:
Claude 2026-08-29 03:11:43 -05:00
parent 64b5e4890e
commit a0b03b87c6
3 changed files with 168 additions and 50 deletions

View File

@ -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"

View File

@ -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 }
}
}
},

View File

@ -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",