From a0b03b87c6139b9c75dcd3e6e9ec1a4ad2af7de6 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 29 Aug 2026 03:11:43 -0500 Subject: [PATCH] =?UTF-8?q?codex:=20mining-f2p=20=E2=80=94=20level-1=20pic?= =?UTF-8?q?kaxe=20tie-break:=20Mining=201=20selects+equips=20iron=20(bounc?= =?UTF-8?q?e-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",