Merge origin/main 5ca8324b into issue-3; reconcile validator gates (governor 10637 / owner guidance 2026-08-29)

Magic/Alchemy PR #10 landed on main; this integrates it into the woodcutting
branch and resolves the predicted tools/validate.py conflict:

- union of all validator gates: magic/alchemy (recipe/rate/tier/loadout,
  supply_limit_conflicts, document_error + _end_to_end_bad_mapping),
  mining (pickaxe suite), GE-flip lifecycle (floor-tax + scheduler +
  conservation + slot-phase + buy-capacity + slot-reuse), and the
  woodcutting gates (canonical axe table, per-mode ladders, per-selected-
  row 27/28 dynamic rates, drop-GP-zero, prepare/failed/drop-gate wiring).
- governor 10637: removed the run_flip_selftest alias duplication — the
  combined GE/Woodcutting suite no longer runs twice. Each suite now runs
  exactly once per invocation: run_selftest (cross-field/alchemy),
  run_flip_selftest (GE lifecycle), run_mining_selftest, and the new
  run_woodcutting_selftest. Truthful unique selftest count: 144 PASS.
- tree scan: 6 entries, 0 errors, 0 warnings; git diff --check clean.
This commit is contained in:
Claude 2026-08-29 15:44:34 -05:00
commit 0c2e38c998
4 changed files with 2275 additions and 270 deletions

View File

@ -3,83 +3,189 @@ title: Low-Level Alchemy (F2P)
domain: economy
tags: [alching, magic-training, f2p, loss-training]
status: verified
version: 1
updated: "2026-08-27" # ISO dates must stay quoted — YAML 1.1 parses bare dates to datetime.date
version: 4 # 4: bounce-fix 6 2026-08-28 (governor comment 10394, PR #10) — real build-loadout action a8 (verified collected staff/candidate/natures, overflow deposit, bank-stock-shortfall-only withdrawal, carried counts PROVEN before the first cast a9; action ids renumbered a9-a13), capital-loss stop sign corrected (initial capital - total assets > 684,400), Magic target pinned at 55 (the LA -> HLA handoff; 54 is a continue), GP bounds tied to the two declared fill scenarios (max-fill net -126.6 -> low -123,700; live-low net -112 -> high -65,600). 3: bounce-fix 4 2026-08-28 (owner guidance 10331, PR #10) — explicit rate_model (session-realized rates incl. startup over a 2h horizon: xp 23,000, gp -109,500..-65,600), complete GE acquisition path (open -> fill -> collect) for the initial buy AND every restock (bank-stock fast path), fail-closed staff equip, monotonic break example (550/1,100/1,650 independent of 901-cast restocks), assets-based capital stops
updated: "2026-08-28" # ISO dates must stay quoted — YAML 1.1 parses bare dates to datetime.date
summary: >
Magic training from level 21 by low-alching F2P items with the highest
lowalch-value among affordable, high-limit items. At current prices it is
a LOSS activity (money in exchange for Magic XP); run it only for XP.
Model pins (owner guidance 2026-08-28): one iteration = one cast; breaks
every 400-700 COMPLETED CASTS (cast-count based, independent of bank
visits — midpoint example 550, 1,100, 1,650, ...); restock when candidate
or nature drops below the numeric restock_threshold (100) against a
carried_batch of 1,000/1,000 (restock cadence 901 casts, monotonic and
independent of breaks). EVERY purchase follows the complete GE path open
-> place -> fill/wait -> collect, for the initial acquisition and for
every restock (a bank-stock fast path applies only when the bank actually
holds enough stock). Staff-of-fire equip failure is FAIL-CLOSED (bounded
retries, then log ERROR + alert + stop — no fire-rune fallback is
modeled). Session horizon 2 h; all rates are SESSION-REALIZED under the
declared rate_model (startup + restock + breaks included; see economy).
accounts:
membership: f2p
min_level: 21 # Magic 21 to cast Low Level Alchemy (wiki-verified)
requires_quest: []
requires_other:
- "Staff of Fire equipped (removes 3 fire runes from the cost)"
- "A bank with GE access within reach"
- "Staff of Fire equipped (removes 3 fire runes from the cost; NO level requirement documented on the OSRS wiki — verified 2026-08-28, see sources). Equip failure is FAIL-CLOSED: bounded retries (3), then log ERROR + alert + stop — there is no fire-rune fallback in this model"
- "A bank with GE access within reach (alch spot = the bank booth; restock trip [60, 120] s)"
- "Starting capital >= ~1.30M gp CASH (staff hard cap 5,000 + first batch 1,000/1,000 at MAX allowed fill 306,600 + session loss reserve 684,400 = 6 worst-case 901-cast batches at live-low+5% fills (covers Magic 21 -> 55) + resting-offer float 306,600). The capital stop checks TOTAL ASSETS (cash + supplies at current buy cost), never raw cash — cash legitimately converts into supplies mid-run"
best_practice:
equipment:
- item: staff of fire
why: "unlimited fire runes — LA rune cost drops from 3 fire + 1 nature to 1 nature per cast (~15gp/cycle saved)"
source: "OSRS wiki — Staff of Fire; Low Level Alchemy (Cost)"
why: "unlimited fire runes — LA rune cost drops from 3 fire + 1 nature to 1 nature per cast (~15gp/cycle saved); equip requirement: none documented on the OSRS wiki (verified 2026-08-28); if it cannot be equipped after bounded retries the run FAILS CLOSED (no fallback modeled)"
source: "OSRS wiki — Staff of fire (id 1387: no requirements row in the infobox, none in the prose — contrast Battlestaff 'requires 30 Attack and 30 Magic'); Low Level Alchemy (Cost)"
techniques:
- name: cast on noted (stacked) items
why: "one inventory slot, minimal mouse travel between spell icon and item"
why: "one inventory slot, minimal mouse travel between spell icon and item; means an inventory does NOT define a batch size — batch sizes are the explicit numeric carried_batch below"
source: "OSRS wiki — Low Level Alchemy (Usage: casting alchemy spells on noted items)"
- name: bank at a GE-adjacent bank
why: "supply restock without a second travel leg"
source: "researched 2026-08-27 (standard operator practice; wiki LA Cost section corroborates rune economy)"
why: "supply restock without a second travel leg — runes and alch items both come from the GE; restock trip 60-120s"
source: "OSRS wiki — Money making guide/High alching profitable free-to-play items (bulk alching via noted items; West Varrock Bank is the common F2P alch/trade hub); OSRS wiki — High Level Alchemy (buying GE items below alch value for break-even/profit; noted-item casting saves bank withdrawals)"
economy:
# Verified 2026-08-27 against prices.runescape.wiki (live 'latest')
# + OSRS wiki Low_Level_Alchemy infobox.
# Bounce-fix 6 (2026-08-28, governor comment 10394): the GP bounds are tied
# to the DECLARED actual-fill scenarios with the SAME costs as the capital
# model — live-low fills (net -112/cast: 148 + 144) and MAX allowed fills
# (live low + 5 %: 155.4 + 151.2 = 306.6 -> net -126.6/cast), because the
# executable purchase accepts live low + 5 % (a4/a10 max_price).
item: Gold necklace # example anchor; rank by cheapest candidate live
item_id: 1654
cost_per_cycle: 292 # necklace buy 148 + nature rune 144 (staff of fire)
cost_per_cycle: 292 # necklace buy 148 + nature rune 144 (staff of fire, live low)
cost_per_cycle_max_fill: 306.6 # live low + 5 %: 155.4 + 151.2 (same costs as the capital model)
income_per_cycle: 180 # lowalch of gold necklace (40% of value 450)
net_per_cycle: -112
gp_per_hour_low: -250000 # negative: this burns GP
gp_per_hour_high: -200000
xp_per_hour: 55800 # 31 xp/cast (wiki) @ ~1800 casts/hr (3-tick, wiki)
net_per_cycle: -112 # at live-low fills
net_per_cycle_max_fill: -126.6 # at MAX allowed fills (worst declared scenario)
gp_per_hour_low: -123700 # session-realized WORST declared scenario: 977.1 casts/hr x -126.6 (max-fill) = -123,700 (fast 3.4s cycle, 300s startup, 2h horizon)
gp_per_hour_high: -65600 # session-realized BEST declared scenario: 585.9 casts/hr x -112 (live-low) = -65,621 (slow 5.2s cycle, 600s startup, 2h horizon)
xp_per_hour: 23000 # session-realized midpoint: 741.8 casts/hr x 31 = 22,995 (pin 23,000)
# ---- rate model (derived; enforced by tools/validate.py rate_model_errors) ----
# Cast: 1.8s (3 ticks x 0.6s, rate_model.cast_ticks) + anti_ban.delay
# [1.6, 3.4]s ADDITIVE (action-grammar.md:135) = 3.4-5.2s, midpoint 4.3s.
# BATCH model: carried_batch 1,000 candidate + 1,000 natures; restock_threshold
# 100 -> 901-cast restock cadence. Overhead per 901-cast batch: restock trip
# [60, 120]s (mid 90) + breaks (every [400, 700] casts, mid 550, for
# [45, 120]s, mid 82.5s -> 82.5 x 901/550 = 135.2s).
# STEADY-STATE casts/hr = 901 x 3600 / (901 x cycle + trip + break_share x 901):
# fast 3.4s -> 1,019.7; midpoint 4.3s -> 791.3; slow 5.2s -> 639.2.
# SESSION-REALIZED (2 h horizon = 7,200 s; startup 300-600 s, mid 450 s ->
# 450/7200 = 6.25 % amortised): 977.1 / 741.8 / 585.9 casts/hr.
# xp_per_hour = 741.8 x 31 = 22,995 (pin 23,000);
# gp_per_hour = casts/hr x net-per-cast over the TWO declared fill
# scenarios (same costs as the capital model):
# worst = fast casts/hr x max-fill net: 977.1 x -126.6 = -123,700 (low)
# best = slow casts/hr x live-low net: 585.9 x -112 = -65,621 (high)
# (mid x mid-fill -119.3 = -88,497 is inside the band)
# BATCH ECONOMICS (901 casts; deposits 901 x 180 = 162,180 gp per batch):
# input cost at live low: 901 x 292 = 263,092 -> net -100,912/batch.
# input cost at MAX fill (live low + 5 %: 1,000 x 155.4 + 1,000 x 151.2 =
# 306,600 per batch): 901 x 306.6 = 276,247 -> net -114,067/batch (worst
# case). Reserve = 6 worst-case batches (Magic 21 -> 55) = 684,400 gp;
# the capital stops check TOTAL ASSETS, not raw cash (accounts + stop_conditions).
# Superseded figures (all fail the derived gate on purpose): loop-only 837
# casts/hr (25,953 xp/hr); the 8-14-cast-break regime (~305 casts/hr); the
# 1,800-cast regime; the pre-startup realized 24,500 (791.3 x 31) — startup
# amortisation is part of the model.
supplies:
- item: gold necklace
qty_per_cycle: 1
source: ge
ge_limit: 18000 # mapping 'limit', wiki-verified
ge_limit: 18000 # mapping 'limit', wiki-verified; one carried_batch of 1,000 fits the 4h window
- item: nature rune
qty_per_cycle: 1
source: ge
ge_limit: 12500
ge_limit: 18000 # mapping 'limit' + Module:Exchange/Nature_rune agree;
# matches magic-alch-handoff (consistency check enforces)
profit_drivers:
- "NONE at current prices — XP is the product, not GP"
- "Pick the candidate with the least-negative net each run (live rank)"
- "Batch-scale economics (worst case 1 nature/cast): carried_batch 1,000/1,000, restock every 901 casts — one batch = 901 x -112 = -100,912 gp at live-low fills (-114,067 at MAX allowed fills), +27,931 xp, deposits 162,180 gp"
# Explicit activity/rate discriminator (owner guidance 2026-08-28, bounce 4):
# this entry OPTS IN to the derived-rate gate; every rate derives from the
# entry's own declared lifecycle and the validator re-derives it.
rate_model:
anchor_tier: la # xp_per_hour pins the LA tier's realized rate
cast_ticks: 3 # LA is a 3-tick cast -> 1.8 s (0.6 s/tick)
restock_trip_s: [60, 120] # GE-adjacent booth trip per restock
session_horizon_s: 7200 # 2 h session; startup is amortised over it
startup_s: [300, 600] # one-time GE acquisition + loadout
alch_carry_batch: 1000 # stacked candidates + natures
alch_restock_threshold: 100 # -> 901-cast LA restock cadence
state_machine:
initial: idle
terminal: [complete, failed]
states:
idle:
goal: reach the bank + have supplies in inventory
next: [casting, bank_supplies]
goal: "session start at the GE bank booth. Open GE (a2), then place the initial offers: staff of fire (a3), candidate 1,000 (a4), nature runes 1,000 (a5) at live low + 5% (staff: +25% up to the 5,000 cap). Record initial capital in the cash ledger (total assets = cash + supplies at current buy cost). Nothing is bought or collected without an open GE interface"
next: [wait_offers, complete, failed]
wait_offers:
goal: "wait for the pending offers (ge_offers_done); when filled, collect every purchase + refund from the collection box (a6 — never leave purchases in the box). PARTIAL FILLS: collect the filled portion, re-place the remainder at the current edge (a4/a5); 3 consecutive no-fill rounds -> failed. An offer still unfilled at session end is left resting by design — its coins are tied up (float reserve) and return when it fills or the operator aborts it (the single operator-touch point)"
next: [ready, wait_offers, failed]
ready:
goal: "equip the staff of fire (a7 — bounded retries, then FAIL CLOSED) and build the VERIFIED loadout (a8 build-loadout): verify the collected staff + candidate + natures (from a6), deposit overflow, withdraw only the real bank-stock shortfall, and PROVE the carried counts equal carried_batch 1,000/1,000 BEFORE the first cast (a9)"
next: [casting, bank_supplies, failed]
casting:
goal: cast Low Level Alchemy on the current candidate item
next: [bank_supplies, idle, complete, failed]
goal: "cast Low Level Alchemy on the current candidate item (a9) — each cast consumes 1 candidate + 1 nature and pays 180 gp (lowalch), +31 xp, net -112 gp at live-low fills; take a break when the completed-cast counter hits run_control.break.every ([400, 700] casts, via a13); the counter is NEVER reset by restocking"
next: [bank_supplies, complete, failed]
bank_supplies:
goal: bank the produced coins, withdraw fresh candidate + runes
next: [casting, idle]
goal: "restock to carried_batch: deposit coins + products and withdraw ALL candidate/natures the bank holds (a10 — bank-stock fast path when the bank already holds enough); for the remaining deficit, open GE (a2), buy the deficit (a11, live low + 5%), collect it (a12) — never withdraw a batch that was never bought. Restocking never resets the cast counter or the break counter; the break cadence (550/1,100/1,650, ...) is independent of the restock cadence (901/1,802, ...)"
next: [casting, bank_supplies, failed]
complete:
goal: reached the run's XP/level target
goal: "reached the run's XP/level target (default 55 — the LA -> HLA handoff) or the 2 h session horizon"
failed:
goal: hard failure — stop, log ERROR sentinel, alert
goal: "hard failure — stop, log ERROR sentinel, alert"
transitions:
- from: casting
when: "candidate item count < buffer OR nature rune count < buffer"
to: bank_supplies
- from: idle
when: "magic level >= target (default 54, hand off to HLA at 55)"
when: "staff + candidate + nature offers all placed (a3/a4/a5)"
to: wait_offers
- from: idle
when: "magic level >= target (55 — the LA -> HLA handoff) at session start"
to: complete
- from: idle
when: "3 consecutive offer-place failures at session start (no fallback candidate)"
to: failed
- from: wait_offers
when: "ge_offers_done AND all purchases collected (a6)"
to: ready
- from: wait_offers
when: "partial fill: filled portion collected (a6), remainder re-placed at the current edge (a4/a5) — still waiting"
to: wait_offers
- from: wait_offers
when: "3 consecutive no-fill rounds (offer cannot fill at the accepted edge)"
to: failed
- from: ready
when: "staff equipped (a7) AND build-loadout verified the carried counts at carried_batch 1,000/1,000 (a8 — collected staff/candidate/natures present, overflow deposited, only a real bank-stock shortfall withdrawn)"
to: casting
- from: ready
when: "staff equip failed after bounded retries — FAIL CLOSED"
to: failed
- from: ready
when: "build-loadout verification shows a carried shortfall (candidate or nature < carried_batch) that the bank does not hold — restock first"
to: bank_supplies
- from: casting
when: "candidate count < restock_threshold (100) OR nature count < restock_threshold (100) — deterministic restock trigger, pinned numbers"
to: bank_supplies
- from: casting
when: "magic level >= target (55 — the LA -> HLA handoff; level 54 is a CONTINUE: LA keeps casting until the handoff boundary)"
to: complete
- from: casting
when: "3 consecutive failed casts (client issue — do not loop)"
to: failed
- from: bank_supplies
when: "restocked to carried_batch (1,000 candidate + 1,000 nature) — bank fast path or re-bought deficit collected"
to: casting
- from: bank_supplies
when: "restock partial: deficit re-bought but offers still filling (ge_offers_done false) — loop the restock phase"
to: bank_supplies
- from: bank_supplies
when: "3 consecutive no-fill restock rounds OR a capital stop fires (assets / loss-reserve checks in stop_conditions)"
to: failed
actions:
- id: a1
@ -87,25 +193,95 @@ actions:
verb: move_to
target: bank
params:
location: "nearest bank with GE"
location: "GE-adjacent bank (alch spot = the bank booth, so restock is a single short leg)"
method: [teleport, walk]
on_fail: retry
anti_ban:
delay: [400, 1600]
- id: a2
step: buy-supplies
step: open-ge
verb: ge_open
target: grand-exchange
params:
note: "open the GE interface at the booth. The entry NEVER buys (a3/a4/a5/a10) or collects (a6/a11) without an open GE interface — the action grammar defines ge_open, ge_buy, ge_collect as separate verbs, so the open step is explicit"
on_fail: retry
anti_ban:
delay: [600, 1400]
- id: a3
step: buy-staff
verb: ge_buy
target: grand-exchange
params:
item: "current candidate item"
qty: "fill to buffer"
max_price: "live low + 5%"
item: "staff of fire"
qty: 1
max_price: "live low + 25 % (wiki value 1,500; hard cap 5,000)"
note: "offer rests until filled or the run ends; on a partial/no fill, collect what filled (a6) and re-place the remainder at the current edge — bounded retries, then the run fails closed (no fire-rune fallback exists)"
on_fail: retry
anti_ban:
delay: [1200, 3000]
- id: a3
- id: a4
step: buy-supplies
verb: ge_buy
target: grand-exchange
params:
item: "current least-loss live candidate (default: gold necklace)"
qty: "carried_batch = 1,000 (fits the 4h GE limit window of 18,000)"
max_price: "live low + 5 %"
note: "offer rests until filled or the run ends; on a partial fill, collect the filled portion (a6) and re-place the remainder at the current edge"
on_fail: retry
anti_ban:
delay: [1200, 3000]
- id: a5
step: buy-natures
verb: ge_buy
target: grand-exchange
params:
item: "nature rune"
qty: "carried_batch = 1,000 (4h GE limit 18,000)"
max_price: "live low + 5 %"
note: "offer rests until filled or the run ends; partial fills handled like a4"
on_fail: retry
anti_ban:
delay: [1200, 3000]
- id: a6
step: collect-purchases
verb: ge_collect
target: grand-exchange
params:
note: "collect every filled purchase and refund from the GE collection box into the inventory — staff, candidate, natures (and the equivalent on every restock). Partial offers: the collected portion is added to the ledger; nothing is left in the box at session start"
on_fail: retry
anti_ban:
delay: [600, 1400]
- id: a7
step: equip-staff
verb: menu_option
target: "staff of fire in inventory"
params:
option: "Wield"
note: "Equip requirement is NONE documented on the OSRS wiki Staff of fire page (no requirements row in the infobox, none in the prose; compare Battlestaff 'requires 30 Attack and 30 Magic'; verified 2026-08-28). FAIL-CLOSED path: bounded retries (3), then log ERROR + alert + stop — no fire-rune fallback is modeled (never loop on equip attempts)"
on_fail: retry
anti_ban:
delay: [600, 1800]
- id: a8
step: build-loadout
verb: bank_inventory
target: bank
params:
deposit: [overflow products]
withdraw: "only the real bank-stock shortfall up to carried_batch (1,000 candidate + 1,000 nature) — the purchases collected by a6 are already in the inventory; a deficit the bank does not actually hold is NEVER withdrawn (it is bought at the GE via the bank_supplies phase, a11)"
verify: "staff of fire collected and present (a6), candidate count == carried_batch 1,000, nature count == carried_batch 1,000 — the carried counts are PROVEN before the first cast (a9); a verified shortfall sends the slot to bank_supplies instead of casting"
on_fail: retry
anti_ban:
delay: [500, 1100]
- id: a9
step: cast-alch
verb: use_item_on
target: "candidate item in inventory"
@ -113,43 +289,153 @@ actions:
spell: "Low Level Alchemy"
item: "cheapest live candidate in inventory"
on: "itself"
consumption: "1 candidate + 1 nature per cast (staff of fire default); pays 180 gp (lowalch) + 31 xp, net -112 gp at live-low fills"
on_fail: retry
anti_ban:
delay: [1600, 3400] # 3-tick cast = ~5.4s game; stay under it, humanised
delay: [1600, 3400] # MANDATORY post-action sleep, ADDITIVE to the 1.8s cast (3 ticks x 0.6s; action-grammar.md:135): cycle = 1.8s + 1.6-3.4s = 3.4-5.2s, midpoint 4.3s -> session-realized ~742 casts/hr incl. startup + restock + breaks (rate_model)
- id: a4
step: bank-loop
- id: a10
step: restock-deposit
verb: bank_inventory
target: bank
params:
deposit: [all coins, all products]
withdraw: "candidate item + nature runes up to buffer"
withdraw: "ALL candidate + nature runes currently in the bank, up to carried_batch (1,000/1,000) — the bank-stock FAST PATH applies only when the bank actually holds enough stock; the remaining deficit is bought at the GE (a11) and collected (a12)"
on_fail: alert
anti_ban:
delay: [500, 1100]
- id: a11
step: restock-buy
verb: ge_buy
target: grand-exchange
params:
item: "current least-loss live candidate + nature runes"
qty: "deficit to carried_batch = carried_batch - inventory count after the a10 fast path (e.g. 901 candidate + 901 nature after a 901-cast batch with an empty bank)"
max_price: "live low + 5 %"
note: "the deficit is RE-BOUGHT at the GE on every restock — the entry never withdraws a batch that was never purchased; partial/failed fills use the same collect + re-place recovery as the initial acquisition"
on_fail: retry
anti_ban:
delay: [1200, 3000]
- id: a12
step: restock-collect
verb: ge_collect
target: grand-exchange
params:
note: "collect the re-bought deficit into the inventory — inventory returns to carried_batch 1,000/1,000 before casting resumes"
on_fail: retry
anti_ban:
delay: [600, 1400]
- id: a13
step: take-break
verb: take_break
target: "current location"
params:
duration: "[45, 120] seconds of idle AFK per break (matches run_control.break.for)"
note: "executed by the translator when the completed-cast counter reaches run_control.break.every — this IS run_control.break's executor, fired on CAST COUNTS, not on bank visits; the cast counter is NOT reset by restocking (breaks land at 550/1,100/1,650 in the midpoint example, independent of the 901-cast restock cadence)"
on_fail: skip
run_control:
iteration: "one cast"
max_iterations: null
iteration: "one cast" # = one Low Level Alchemy cast (+31 xp, -112 gp at the anchor). Per docs/schema.md run_control, break.every counts ITERATIONS — so it counts CASTS here, NOT bank trips (owner decision 2026-08-28)
max_iterations: null # null = until complete condition
break:
every: [8, 14] # bank cycles (breaks land on bank visits)
for: [45, 120] # seconds of idle AFK
every: [400, 700] # COMPLETED CASTS between AFK breaks (midpoint 550 = ~39 min at the 4.3s midpoint cycle). Cast-count-based and independent of bank visits; a restock trip NEVER counts as a break and does not reset this counter
for: [45, 120] # seconds of idle AFK per break
stop_conditions:
- "magic level >= target"
- "gp under floor"
- "magic level >= target (55 — the LA -> HLA handoff)"
- "session elapsed >= 2 h (session horizon — startup is amortised over this window in the rate model)"
- "total assets (cash + supplies at current buy cost) < 311,600 — cannot fund the next batch at max fill (306,600) plus staff replacement (5,000); assets, NOT raw cash: cash that became supplies is not a loss"
- "session net loss = initial capital - total assets > 684,400 — the 6-batch worst-case loss reserve is exhausted (a loss is WHEN ASSETS FALL; total assets - initial capital goes NEGATIVE on losses and can never exceed the reserve — the reversed sign never fires)"
- "3 consecutive failed casts (client issue — do not loop)"
- "3 consecutive no-fill GE rounds (initial acquisition or restock) with no fallback candidate available"
- "staff of fire equip failed after bounded retries — FAIL CLOSED (log ERROR, alert, stop)"
# ================= WORKED EXAMPLE (pinned, deterministic — owner guidance 2026-08-28) =================
# Constants: carried_batch = 1,000 candidate + 1,000 nature; restock_threshold = 100;
# break.every = [400, 700] casts — the example pins the MIDPOINT 550;
# break.for = [45, 120] s; restock cadence = 901 casts (1,000 - 99); the
# staff-of-fire path consumes 1 candidate + 1 nature per cast (worst case 1:1).
# The two counters are MONOTONIC and INDEPENDENT: breaks at 550, 1,100, 1,650,
# 2,200... (multiples of 550) and restocks at 901, 1,802, 2,703... (multiples
# of 901) — gcd(550, 901) = 1, so they interleave forever and restocking NEVER
# resets or shifts the break counter.
#
# Step 0 — preparation (one-time startup, ~300-600 s): account holds >= ~1.30M
# gp cash (staff cap 5,000 + first batch max 306,600 + reserve 684,400 +
# float 306,600). At the GE bank: open GE (a2); place offers — staff (a3),
# 1,000 gold necklaces (a4), 1,000 nature runes (a5); wait for fills
# (ge_offers_done) and collect everything (a6); equip the staff (a7); loadout
# = carried_batch 1,000/1,000 (a8). Cast counter := 0; cash ledger opened.
# Step 1 — casts #1..#550: each cast consumes 1 candidate + 1 nature (-292 gp)
# and pays 180 gp (lowalch) + 31 xp -> net -112 gp. Cast counter 0 -> 550
# (~39 min at midpoint).
# Step 2 — FIRST SCHEDULED BREAK at cast 550 (the interval since cast 0):
# take-break 45-120 s (a13). Resume casting at cast 551. The break fired on
# the CAST COUNT, not on any bank visit.
# Step 3 — casts #551..#901: countdown candidate 1,000-901 = 99 < 100
# (restock_threshold) and nature 99 < 100 simultaneously (worst case 1:1) ->
# the transition fires at the end of cast #901: FIRST RESTOCK at cast 901.
# Deposit 901 x 180 = 162,180 gp (a10); the bank holds 0 supplies (all sales
# were GE buys), so the fast path yields nothing -> open GE (a2), RE-BUY the
# deficit 901 candidate + 901 nature (a11, live low + 5 %), collect (a12) ->
# inventory back to 1,000/1,000. Batch net: -100,912 gp (live-low fills) or
# -114,067 gp (max fills). The cast counter (901) and break counter continue.
# Step 4 — SECOND BREAK at cast 1,100: the interval since break #1 is exactly
# 550 casts (1,100 - 550); the cast-901 restock did NOT reset or shift it.
# Step 5 — THIRD BREAK at cast 1,650 (1,100 + 550); SECOND RESTOCK at cast
# 1,802 (901-cadence, 1,802 = 901 x 2). The sequences 550/1,100/1,650/2,200
# and 901/1,802/2,703 interleave independently.
# Step 6 — continue until a stop_condition fires: Magic 55 (the LA -> HLA
# handoff — level 54 CONTINUES casting), the 2 h session horizon, the
# capital stops (assets floor 311,600 or initial capital - total assets >
# 684,400), 3 failed casts, or 3 consecutive no-fill GE rounds.
# ============================================================================================
risk:
ban_level: low
notes:
- "High cast cadence is the main detection vector; keep anti_ban delays."
- "GE buy bursts: pace offers, respect the 4h limit window."
- "High cast cadence is the main detection vector; keep the mandatory anti_ban delays (cadence pin: additive post-cast sleep [1.6, 3.4]s; session-realized ~742 casts/hr midpoint incl. startup + restock + breaks over a 2 h session; steady-state ~791; loop-only 837 is a ceiling reference, never the output)."
- "Breaks are CAST-COUNT-based (every [400, 700] completed casts, 45-120s AFK) and independent of bank visits — never treat a restock trip as a break, and never reset the break counter on restock (gcd(550, 901) = 1)."
- "GE buy bursts: pace offers, respect the 4h limit window (18,000 necklaces / 18,000 natures); offers persist until filled or aborted and a 4h limit reset is NOT an offer expiry."
# --- recovery block (owner guidance 2026-08-28: every activity must cover its complete runnable lifecycle) ---
# - Missing materials / depleted supplies: the restock_threshold transition (casting ->
# bank_supplies at candidate OR nature < 100) catches both mid-run depletions; the
# restock phase (a9 -> a2 -> a10 -> a11) rebuilds to carried_batch and the loop
# resumes (worked example steps 3-4). The entry NEVER withdraws a batch that was
# never purchased — every restock re-buys the deficit at the GE.
# - GE offers: partial fills are collected (a6/a11) and the remainder is re-placed at
# the current edge (a3/a4/a5/a10); after 3 consecutive no-fill rounds (initial or
# restock) the run stops (failed) — never sit in an unfillable offer loop. Unfilled
# offers at session end are left resting by design (float reserve; coins return when
# they fill or the operator aborts them — the single operator-touch point).
# - Staff of fire cannot be equipped (a7): bounded retries (3), then FAIL CLOSED —
# log ERROR + alert + stop. No fire-rune fallback is modeled (it would need its own
# purchase/supply/rate path; the owner decision rejects a prose-only fallback).
# - Invalid loadout (wrong item in hand / spell unavailable / carried counts below
# carried_batch): re-run build-loadout (a8); a verified shortfall the bank does
# not hold goes to bank_supplies; 3 consecutive failed casts -> failed.
# - Capital: the stop checks TOTAL ASSETS (cash + supplies at current buy cost), and
# both capital stops are assets-based (see stop_conditions) — cash converted into
# supplies is not a loss and never fires a stop by itself.
# - Explicit completion/stop: stop_conditions above (Magic 55 LA -> HLA handoff,
# 2 h session horizon, assets floor 311,600, loss reserve 684,400, repeated
# failures, fail-closed staff equip).
sources:
- "OSRS wiki — Low Level Alchemy (level 21, 31 xp/cast, 3-tick, F2P; infobox 2026-08-27)"
- "prices.runescape.wiki mapping (id 1654 gold necklace: members=false, limit 18000, lowalch 180, highalch 270, value 450; id 561 nature rune; id 554 fire rune)"
- "prices.runescape.wiki latest 2026-08-27 (necklace buy 148, nature buy 144, fire buy 5)"
verified: "2026-08-27" # keep quoted (YAML 1.1 date parsing)
- "OSRS wiki — Grand Exchange (offers persist until filled or aborted; filled items and coin refunds are collected from the booth/collection box; partial fills are separately collectable) — verified 2026-08-28"
- "OSRS wiki — Grand Exchange/Buying limits (4-hour window; the limit resets 4 hours after the FIRST unit is bought; outstanding offers remain active and a limit reset is NOT an offer expiry) — verified 2026-08-28"
- "OSRS wiki — Staff of fire (page verified 2026-08-28 for the equip requirement: id 1387, F2P item, tradeable, options Wield — NO requirements row in the Infobox Item and NO requirement statement in the prose; contrast Battlestaff, whose prose states 'requires 30 Attack and 30 Magic to wield')"
- "docs/schema.md run_control (iteration unit; break.every counts iterations) + docs/action-grammar.md:135 (anti_ban.delay = additive post-action sleep) + verbs ge_open/ge_buy/ge_collect/ge_offers_done (separate GE primitives) — the cadence + break + GE + lifecycle model; owner guidance 2026-08-28 (rate_model, fail-closed staff, monotonic counters); governor comment 10394 (build-loadout verified action, sign-corrected loss stop, 55 handoff boundary, fill-scenario GP bounds)"
verified: "2026-08-27" # keep quoted (YAML 1.1 date parsing); prices last re-fetched live 2026-08-27
related:
- magic-alch-handoff
- high-alch-p2p
- f2p-alch-catalog
- mule-topup

View File

@ -0,0 +1,652 @@
id: magic-alch-handoff
title: Magic training via alchemy (LA → SII → HLA handoff)
domain: training
tags: [magic-training, alching, f2p, xp-training]
status: verified
version: 5 # 5: bounce-fix 7 2026-08-29 (governor comment 10477, PR #10) — EXECUTABLE tier boundary: every tier predicate is mutually exclusive — SII only while 43 <= Magic < 55, HLA only when Magic >= 55 AND the session target is above 55, LA below 43 or the explicit SII-skip path in 43-54, completion FIRST when the configured target is reached (no Magic level can match two tiers; the old "43-55" SII window overlapped HLA at exactly 55). 4: bounce-fix 6 2026-08-28 (governor comment 10394) — build-loadout a7 VERIFIES collected staff/supplies and PROVES carried counts before the first cast (ready/ready->cast strengthened), capital-loss stop sign corrected (initial capital - total assets > 690,075). 3: bounce-fix 4 2026-08-28 (owner guidance 10331) — explicit rate_model discriminator (derived per-entry lifecycle rates), per-recipe inventory capacity floor(27/qty) with derived min/mid/max rates pinned in recipes.rates, complete GE acquisition path (open -> fill -> collect) for initial buy AND every restock (bank-stock fast path), fail-closed staff equip (bounded retries, no fire-rune fallback), configurable session target (55 = LA/SII->HLA handoff completion; >55 = executable HLA tier to the target; LA stays the 43-54 fallback), capital model (cash vs supplies, max-fill costs, reserve, assets-based stops)
updated: "2026-08-28" # ISO dates must stay quoted — YAML 1.1 parses bare dates to datetime.date
summary: >
The standard F2P alchemy training path: Low Level Alchemy from level 21
(31 xp/cast), an optional Superheat Item window from 43 (53 xp/cast, smelts
ore into bars), then High Level Alchemy from 55 (65 xp/cast, the wiki's
recommended end-state). All three spells are F2P and stay castable after
their unlock levels, so the handoffs are efficiency recommendations, not
hard caps. The SESSION TARGET is configurable: the default 55 completes at
the LA/SII -> HLA handoff; a target above 55 transitions into the
executable HLA tier and continues until the target — HLA is never
declared-but-unreachable, and Low Alchemy remains the executable fallback
throughout Magic 43-54 whenever SII is skipped. SII model (owner guidance
2026-08-28): a dynamic recipe table governed by the account's Smithing
level — the highest UNLOCKED recipe whose complete recipe cost (ALL inputs
at ALLOWED fill prices, live low + 5%) plus one nature rune stays under the
bar's live GE price is selected; its primary ore (target_input, never coal)
is the spell target; materials unobtainable -> next lower unlocked recipe,
else skip SII and stay on LA. SII inventory cycles are CAPACITY-DERIVED:
floor(27 / total input qty) casts per bank cycle (bronze 13, iron/silver/
gold 27, steel 9, mithril 5, adamantite 3, runite 3) — never a fixed pack.
Rates are SESSION-REALIZED under the declared rate_model: cast cycle +
mandatory additive delays + breaks + banking + startup over a 2 h horizon
(anchors: LA 23,000 / SII steel 12,400 / HLA 48,200 Magic XP/hr; per-recipe
min/mid/max rates pinned in the recipes table and enforced by the
validator). Every purchase follows the complete GE path open -> place ->
fill/wait -> collect, for the initial acquisition and every restock (bank
fast path only when the bank holds stock). Staff-of-fire equip failure is
FAIL-CLOSED (bounded retries, then log ERROR + alert + stop).
accounts:
membership: f2p # LA, SII and HLA are all members=No spells
min_level: 21 # Magic 21 — Low Level Alchemy unlock
requires_quest: []
requires_other:
- "Staff of Fire equipped (drops LA/SII/HLA rune cost to 1 nature rune per cast; NO level requirement documented on the OSRS wiki — verified 2026-08-28, see sources). Equip failure is FAIL-CLOSED: bounded retries (3), then log ERROR + alert + stop — there is no fire-rune fallback in this model"
- "SII tier requires the selected recipe's Smithing level (recipes table: bronze 1 / iron 15 / silver 20 / steel 30 / gold 40 / mithril 50 / adamantite 70 / runite 85) — the highest UNLOCKED recipe is selected and an unmet gate is NEVER selected (selection.mode)"
- "Starting capital >= ~1.31M gp CASH for the LA/HLA tier path (staff hard cap 5,000 + first batch 1,000/1,000 at MAX allowed fill 307,650 + session loss reserve 690,075 = 6 worst-case 901-cast batches at live-low+5% fills (covers Magic 21 -> 55) + resting-offer float 307,650). The SII window uses capacity-sized packs (steel example: 9 x (1 iron 105 + 2 coal 157.5 + 1 nature 152.25) = 5,150 gp per pack at max fill). The capital stop checks TOTAL ASSETS (cash + supplies at current buy cost), never raw cash — cash legitimately converts into supplies"
- "Session target Magic level configured at session start: default 55 (completes at the LA/SII -> HLA handoff); a target above 55 runs the HLA tier until the target; LA remains the executable fallback throughout 43-54 when SII is skipped"
- "A bank with GE access within reach (alch spot = the bank booth; restock trips [60, 120] s)"
best_practice:
equipment:
- item: staff of fire
why: "unlimited fire runes — the rune cost of every alchemy spell drops to a single nature rune per cast; equip requirement: none documented on the OSRS wiki (verified 2026-08-28); if it cannot be equipped after bounded retries the run FAILS CLOSED (no fallback modeled)"
source: "OSRS wiki — High Level Alchemy (rune cost table); OSRS wiki — Staff of fire (id 1387: no requirements row in the infobox, none in the prose — contrast Battlestaff 'requires 30 Attack and 30 Magic')"
techniques:
- name: hand off LA -> HLA at level 55 (configurable target)
why: "LA page: 'Players with level 55 Magic are generally recommended to use High Level Alchemy instead, as it provides more coins and experience per cast' — 65 vs 31 xp/cast, 60% vs 40% of item value. The default session target is 55; a target above 55 keeps casting HLA (65 xp/cast) until the configured level — the HLA tier is never declared but unreachable"
source: "OSRS wiki — Low Level Alchemy (Comparison with High Level Alchemy); High Level Alchemy (infobox: level 55, 65 xp)"
- name: optional Superheat Item window 43-54 (43 <= Magic < 55)
why: "SII unlocks at 43 (53 xp/cast) and smelts ore into bars without a furnace — a training window that can profit when ore/bar margins are positive; it cannot be cast on noted ore, so it needs the exact recipe inputs in inventory rather than a note stack. The inventory holds floor(27 / total input qty) casts per bank cycle (steel = 9) — never a fixed 24-cast pack"
source: "OSRS wiki — Superheat Item (43 Magic, 53 xp, F2P; 'smelt ore into bars without a furnace'; 'cannot be cast on noted ore')"
- name: select the highest UNLOCKED SII recipe, never an unmet gate
why: "at each cast the active recipe = the recipes-table entry with the highest smithing_level <= current Smithing whose complete input cost (ALL inputs at ALLOWED fill prices = live low + 5%) + one nature rune at allowed fill stays under the bar's live GE price; the spell targets that recipe's declared target_input (the primary ore — never coal); when the selected recipe's materials cannot be obtained, fall back to the next lower unlocked recipe; when none has a positive margin, skip SII and stay on LA (LA is executable throughout 43-54)"
source: "OSRS wiki — Superheat Item (requires the Smithing level and materials of the bar); Smithing (bar smelting table); prices.runescape.wiki (live GE prices); owner guidance 2026-08-28 (selection.mode, allowed-fill eligibility)"
- name: buy GE items below alch value, cast on noted items at a bank
why: "HLA page: 'Players who train this way can break even on the cost of runes, or even turn a profit, by buying items on the Grand Exchange for a lower cost than the item's High Alchemy value'; all alchemy spells work on noted items, saving bank withdrawals per cast"
source: "OSRS wiki — High Level Alchemy (GE buying for break-even/profit; noted-item casting)"
- name: treat the handoffs as soft efficiency choices
why: "all three spells remain castable past 21/43/55 and stay on the standard F2P spellbook, so the tier switches optimise xp/cast and cost rather than gate the path; LA is the executable fallback whenever the SII window is skipped"
source: "OSRS wiki — Low Level Alchemy; Superheat Item; High Level Alchemy (level + experience infoboxes, members=No)"
economy:
# Per-cast economics at 2026-08-27 snapshot (prices.runescape.wiki latest):
# staff of fire -> 1 nature rune per cast. Nature rune id 561 buy 145
# (mapping limit 18000, cross-checked against Module:Exchange/Nature_rune,
# members=false, lowalch 72 = 40% of value 180 — consistency assertion).
# Income/net per cast depend on the alched item or the recipe bar, so they
# live in the per-activity entries and in the recipes table (this entry
# prices the shared consumable + the rate model).
# xp_per_hour pins the rate_model ANCHOR tier (sii, steel-bar): the entry's
# own declared lifecycle — no hard-coded per-activity constants.
item: nature rune
item_id: 561
cost_per_cycle: 145 # one nature rune per cast (staff of fire), buy price 2026-08-27
xp_per_hour: 12400 # SII anchor, SESSION-REALIZED: steel mid 249.1 casts/hr x 53 x startup factor 0.9375 = 12,379 (pin 12,400)
# ---- rate model (derived from rate_model + actions + run_control + recipes;
# enforced by the validator's rate_model_errors gate) ----
# Cast: 1.8s (3 ticks x 0.6s, rate_model.cast_ticks) + anti_ban.delay
# [1.6, 3.4]s ADDITIVE (action-grammar.md:135) = 3.4-5.2s, midpoint 4.3s.
# Restock trip [60, 120]s (rate_model.restock_trip_s); breaks every
# [400, 700] casts for [45, 120]s (run_control.break) -> amortised break
# share 45/700 fast, 82.5/550 mid, 120/400 slow.
# ALCH tiers (LA/HLA): 901-cast restock batch (carried_batch 1,000 -
# restock_threshold 100), steady-state 1,019.7 / 791.3 / 639.2 casts/hr;
# SESSION-REALIZED over the 2 h horizon with startup 300-600s (factor
# 0.9583/0.9375/0.9167): 977.1 / 741.8 / 585.9 casts/hr ->
# LA 23,000 xp/hr = 31 x 741.8 (22,995)
# HLA 48,200 xp/hr = 65 x 741.8 (48,217)
# SII tiers: per-recipe capacity = floor(27 / total input qty) (steel 9,
# bronze 13, iron/silver/gold 27, mithril 5, adamantite/runite 3); full
# derived min/mid/max rates live in each recipe's rates block (validator-
# checked): steel steady mid 249.1 casts/hr = 13,204 Magic XP/hr; the
# xp_per_hour anchor adds the startup factor (0.9375 mid) -> 12,400.
# Superseded figures (all fail the derived gate on purpose): loop-only
# 837 casts/hr; the 8-14-cast-break regime (~305 casts/hr); the 1,400/
# 1,800-cast regimes; the impossible 24-cast SII pack (518.0 casts/hr) —
# capacity is floor(27/qty), never a fixed pack.
supplies:
- item: nature rune
qty_per_cycle: 1
source: ge
ge_limit: 18000 # mapping + Module:Exchange agree
profit_drivers:
- "XP per cast by tier (wiki infoboxes): LA 31 (level 21) / SII 53 (level 43) / HLA 65 (level 55); SII also grants the bar's Smithing XP (recipes table)"
- "HLA can break even or profit on GE-bought items priced below their High Alchemy value (HLA page); LA is a loss at current prices (see low-alch-f2p)"
- "SII turns a profit only when the complete recipe cost (ALL inputs at ALLOWED fill prices + one nature rune at allowed fill) stays under the resulting bar's GE price — re-check margins per window and select the highest UNLOCKED recipe with a positive margin; never a steel-only default, never an unmet Smithing gate"
# Selection model (owner guidance 2026-08-28): the Superheat recipe is chosen
# at runtime from the recipes table — never hardcoded to one bar, never above
# the account's Smithing level, with a deterministic fallback chain. The
# validator proves eligibility (smithing gate + complete-recipe margin),
# target_input (declared input, never coal), unique gates, and the fallback
# path via its fixtures.
selection:
mode: highest-unlocked
eligible_rule: "at each cast, the active recipe = the recipes-table entry with the highest smithing_level <= current Smithing level whose complete input cost at ALLOWED fill prices (every input at live GE low + 5%, the a4 max_price cap) + one nature rune at allowed fill < the bar's live GE price — the full actual/allowed recipe cost, not a headline ore price"
fallback: "when the selected recipe's materials cannot be obtained (3 consecutive failed GE purchases / unfillable offers), drop to the next lower unlocked recipe and re-check its allowed-fill margin; when no unlocked recipe has a positive margin, skip SII for this window and stay on LA throughout 43-54 — LA remains executable (a8 handles level < 43 AND the 43-54 SII-skip window); never select a recipe whose Smithing requirement is unmet, never cast on coal"
# Concrete F2P bar recipes for Superheat Item. SII requires the bar's
# Smithing level and its exact materials, so every selectable bar is modelled
# with ALL inputs and quantities (id 453 coal appears in steel/mithril/
# adamantite/runite). Levels, xp and item ids from the OSRS wiki (Smithing
# smelting table; Superheat Item grants the bar's Smithing XP per cast).
# target_input = the PRIMARY ORE the spell is cast on (a declared input,
# never coal — the validator enforces both).
# rates (validator-checked, derived from the entry's rate_model): capacity =
# floor(27 / total input qty) casts per bank cycle (28-slot inventory minus
# the nature stack); casts_per_hour and magic_xp_per_hour are the STEADY-
# STATE min/mid/max (cycle endpoints 3.4/4.3/5.2s, trip 60/90/120s,
# break share 45/700, 82.5/550, 120/400). The economy.xp_per_hour anchor
# adds the startup factor (see rate_model).
# econ 2026-08-27: bar margin = bar GE price - (all inputs at allowed fill +
# one nature rune at allowed fill); the client selects via selection.mode.
recipes:
bronze-bar:
bar: Bronze bar
bar_item_id: 2349
smithing_level: 1
smithing_xp: 6.2
target_input: Copper ore
inputs:
- item: Copper ore
item_id: 436
qty: 1
source: ge
- item: Tin ore
item_id: 438
qty: 1
source: ge
rates:
capacity: 13
casts_per_hour: {min: 445.6, mid: 316.5, max: 244.4}
magic_xp_per_hour: {min: 23617, mid: 16776, max: 12952}
iron-bar:
bar: Iron bar
bar_item_id: 2351
smithing_level: 15
smithing_xp: 12.5
target_input: Iron ore
inputs:
- item: Iron ore
item_id: 440
qty: 1
source: ge
rates:
capacity: 27
casts_per_hour: {min: 633.1, mid: 462.5, max: 362.0}
magic_xp_per_hour: {min: 33554, mid: 24514, max: 19187}
silver-bar:
bar: Silver bar
bar_item_id: 2355
smithing_level: 20
smithing_xp: 13.7
target_input: Silver ore
inputs:
- item: Silver ore
item_id: 442
qty: 1
source: ge
rates:
capacity: 27
casts_per_hour: {min: 633.1, mid: 462.5, max: 362.0}
magic_xp_per_hour: {min: 33554, mid: 24514, max: 19187}
steel-bar:
bar: Steel bar
bar_item_id: 2353
smithing_level: 30
smithing_xp: 17.5
target_input: Iron ore
inputs:
- item: Iron ore
item_id: 440
qty: 1
source: ge
- item: Coal
item_id: 453
qty: 2
source: ge
rates:
capacity: 9
casts_per_hour: {min: 355.3, mid: 249.1, max: 191.2}
magic_xp_per_hour: {min: 18833, mid: 13204, max: 10131}
gold-bar:
bar: Gold bar
bar_item_id: 2357
smithing_level: 40
smithing_xp: 22.5
target_input: Gold ore
inputs:
- item: Gold ore
item_id: 444
qty: 1
source: ge
rates:
capacity: 27
casts_per_hour: {min: 633.1, mid: 462.5, max: 362.0}
magic_xp_per_hour: {min: 33554, mid: 24514, max: 19187}
mithril-bar:
bar: Mithril bar
bar_item_id: 2359
smithing_level: 50
smithing_xp: 30
target_input: Mithril ore
inputs:
- item: Mithril ore
item_id: 447
qty: 1
source: ge
- item: Coal
item_id: 453
qty: 4
source: ge
rates:
capacity: 5
casts_per_hour: {min: 232.8, mid: 160.4, max: 122.0}
magic_xp_per_hour: {min: 12338, mid: 8499, max: 6468}
adamantite-bar:
bar: Adamantite bar
bar_item_id: 2361
smithing_level: 70
smithing_xp: 37.5
target_input: Adamantite ore
inputs:
- item: Adamantite ore
item_id: 449
qty: 1
source: ge
- item: Coal
item_id: 453
qty: 6
source: ge
rates:
capacity: 3
casts_per_hour: {min: 153.4, mid: 104.5, max: 79.1}
magic_xp_per_hour: {min: 8131, mid: 5538, max: 4194}
runite-bar:
bar: Runite bar
bar_item_id: 2363
smithing_level: 85
smithing_xp: 50
target_input: Runite ore
inputs:
- item: Runite ore
item_id: 451
qty: 1
source: ge
- item: Coal
item_id: 453
qty: 8
source: ge
rates:
capacity: 3
casts_per_hour: {min: 153.4, mid: 104.5, max: 79.1}
magic_xp_per_hour: {min: 8131, mid: 5538, max: 4194}
# Explicit activity/rate discriminator (owner guidance 2026-08-28, bounce 4):
# this entry OPTS IN to the derived-rate gate; every rate below derives from
# the entry's own declared lifecycle, and the validator re-derives it.
rate_model:
anchor_tier: sii # xp_per_hour pins the SII tier's realized rate
anchor_recipe: steel-bar # the anchor recipe (must be a declared recipe)
cast_ticks: 3 # LA/SII/HLA are 3-tick casts -> 1.8 s (0.6 s/tick)
restock_trip_s: [60, 120] # GE-adjacent booth trip per restock
session_horizon_s: 7200 # 2 h session; startup is amortised over it
startup_s: [300, 600] # one-time GE acquisition + loadout
alch_carry_batch: 1000 # LA/HLA tier: stacked candidates + natures
alch_restock_threshold: 100 # -> 901-cast LA/HLA restock cadence
sii_capacity_slots: 27 # 28-slot inventory minus the nature stack
state_machine:
initial: idle
terminal: [complete, failed]
states:
idle:
goal: "session start at the GE bank booth; choose the session Magic target (default 55 — completes at the LA/SII -> HLA handoff; above 55 the HLA tier runs to the target). Open GE (a2), place the initial offers (staff a3, tier supplies a4), record initial capital in the cash ledger (total assets = cash + supplies at current buy cost). Nothing is bought or collected without an open GE interface"
next: [wait_offers, complete, failed]
wait_offers:
goal: "wait for the pending offers (ge_offers_done); when filled, collect every purchase + refund from the collection box (a5 — never leave purchases in the box). PARTIAL FILLS: collect the filled portion, re-place the remainder at the current edge (a3/a4); 3 consecutive no-fill rounds -> failed. An offer still unfilled at session end is left resting by design — its coins are tied up (float reserve) and return when it fills or the operator aborts it (the single operator-touch point)"
next: [ready, wait_offers, failed]
ready:
goal: "equip the staff of fire (a6 — bounded retries, then FAIL CLOSED) and build the VERIFIED loadout (a7 build-loadout): verify the collected staff + tier supplies (from a5), deposit overflow, withdraw only the real bank-stock shortfall, and PROVE the carried counts equal the tier's carried amounts BEFORE the first cast (a8/a9)"
next: [cast, bank_supplies, failed]
cast:
goal: "cast the active tier spell with the tier-correct target — LA (< 43, or 43-54 when SII is skipped) or HLA (>= 55, only when the session target is above 55) on a noted alch candidate (a8); SII (43 <= Magic < 55, selection.mode) on the selected recipe's declared target_input (primary ore, never coal) with its exact allowed-fill inputs withdrawn (a9). The tier predicates are MUTUALLY EXCLUSIVE: at any Magic level exactly one tier is selectable — SII only while 43 <= Magic < 55, HLA only when Magic >= 55 and the target is higher, completion FIRST when the target is reached (no level can match two tiers; the old '43-55' SII window overlapped HLA at exactly 55). A break fires on the completed-cast counter (a13), NEVER on bank visits, and restocking never resets it"
next: [cast, bank_supplies, complete, failed]
bank_supplies:
goal: "restock to the tier's carried amounts: deposit coins + smelted bars and withdraw ALL tier supplies the bank holds (a10 — bank-stock fast path only when the bank actually holds enough); for the remaining deficit, open GE (a2), buy the deficit (a11, live low + 5%), collect it (a12) — never withdraw a batch that was never purchased. Restocking never resets the cast or break counters"
next: [cast, bank_supplies, failed]
complete:
goal: "magic level >= the configured session target (default 55 — completes at the LA/SII -> HLA handoff; a target above 55 finishes only after the executable HLA tier reaches it)"
failed:
goal: "hard failure — stop, log ERROR sentinel, alert"
transitions:
- from: idle
when: "staff + tier supply offers all placed (a3/a4)"
to: wait_offers
- from: idle
when: "magic level >= session target at session start"
to: complete
- from: idle
when: "3 consecutive offer-place failures at session start (no fallback candidate/recipe)"
to: failed
- from: wait_offers
when: "ge_offers_done AND all purchases collected (a5)"
to: ready
- from: wait_offers
when: "partial fill: filled portion collected (a5), remainder re-placed at the current edge (a3/a4) — still waiting"
to: wait_offers
- from: wait_offers
when: "3 consecutive no-fill rounds (offer cannot fill at the accepted edge)"
to: failed
- from: ready
when: "staff equipped (a6) AND build-loadout verified the carried counts at the tier's carried amounts (a7 — collected staff/supplies present, overflow deposited, only a real bank-stock shortfall withdrawn; carried counts PROVEN)"
to: cast
- from: ready
when: "staff equip failed after bounded retries — FAIL CLOSED"
to: failed
- from: ready
when: "build-loadout verification shows a carried shortfall the bank does not hold — restock first"
to: bank_supplies
- from: cast
when: "level < 43, or (43 <= level < 55 with SII skipped) — LA tier: cast on the cheapest alch candidate (LA is executable below 43 and throughout the 43-54 window only when a recipe-margin issue explicitly skips SII; at Magic 55+ the LA predicate is never true — no level matches two tiers)"
to: cast
- from: cast
when: "43 <= level < 55 and the SII window is chosen (MUTUALLY EXCLUSIVE with HLA — SII is selectable ONLY below 55, never at 55+) — selection.mode picks the recipes-table entry with the highest smithing_level <= current Smithing and a positive allowed-fill complete-cost margin; cast on its target_input"
to: cast
- from: cast
when: "43 <= level < 55 — SII fallback: the selected recipe's materials cannot be obtained, so drop to the next lower unlocked recipe (never an unmet Smithing gate) and continue SII if its margin is positive"
to: cast
- from: cast
when: "43 <= level < 55 and no unlocked recipe has a positive margin this window — skip SII and stay on LA"
to: cast
- from: cast
when: "level >= 55 and session target > 55 — the HLA tier executes (a8) until magic reaches the target (MUTUALLY EXCLUSIVE: HLA is selectable only when the target is ABOVE 55, so an account at 55 with target 55 completes first rather than matching SII/HLA)"
to: cast
- from: cast
when: "LA/HLA tier: candidate count < restock_threshold (100) OR nature count < restock_threshold (100); SII tier: bars_in_inventory >= the selected recipe's capacity (floor(27 / total input qty): steel 9, bronze 13, mithril 5, ...) OR any selected-recipe input < 1 — deterministic restock triggers, pinned numbers"
to: bank_supplies
- from: cast
when: "magic level >= session target (default 55 — completes at the LA/SII -> HLA handoff; targets above 55 require the executable HLA tier FIRST — completion is evaluated before tier selection, so the configured target always wins over any remaining tier predicate)"
to: complete
- from: cast
when: "3 consecutive failed casts (client issue — do not loop)"
to: failed
- from: bank_supplies
when: "restocked to the tier's carried amounts (bank fast path or re-bought deficit collected)"
to: cast
- from: bank_supplies
when: "restock partial: deficit re-bought but offers still filling (ge_offers_done false) — loop the restock phase"
to: bank_supplies
- from: bank_supplies
when: "3 consecutive no-fill restock rounds OR a capital stop fires (assets / loss-reserve checks in stop_conditions)"
to: failed
actions:
- id: a1
step: reach-bank
verb: move_to
target: bank
params:
location: "GE-adjacent bank (alch spot = the bank booth, so restock is a single short leg, 60-120s)"
method: [teleport, walk]
on_fail: retry
anti_ban:
delay: [400, 1600]
- id: a2
step: open-ge
verb: ge_open
target: grand-exchange
params:
note: "open the GE interface at the booth. The entry NEVER buys (a3/a4/a11) or collects (a5/a12) without an open GE interface — the action grammar defines ge_open, ge_buy, ge_collect as separate verbs, so the open step is explicit"
on_fail: retry
anti_ban:
delay: [600, 1400]
- id: a3
step: buy-staff
verb: ge_buy
target: grand-exchange
params:
item: "staff of fire"
qty: 1
max_price: "live low + 25 % (wiki value 1,500; hard cap 5,000)"
note: "offer rests until filled or the run ends; on a partial/no fill, collect what filled (a5) and re-place the remainder at the current edge — bounded retries, then the run fails closed (no fire-rune fallback exists)"
on_fail: retry
anti_ban:
delay: [1200, 3000]
- id: a4
step: buy-supplies
verb: ge_buy
target: grand-exchange
params:
item: "nature runes + the tier's supplies — LA/HLA: the current least-loss alch candidate; SII: the exact unnoted inputs of the SELECTED recipe (every input and quantity from the recipes table, e.g. steel-bar = 1 iron ore + 2 coal), only while the allowed-fill complete-cost margin is positive"
qty: "LA/HLA: carried_batch 1,000 (4h GE limits: natures 18,000, candidates per mapping); SII: one capacity-sized pack of the selected recipe — capacity = floor(27 / total input qty) casts (steel = 9, so 9 x each input quantity + 9 natures per pack)"
max_price: "live low + 5 %"
note: "offer rests until filled or the run ends; partial fills are collected (a5) and the remainder re-placed at the current edge; 3 consecutive no-fills -> SII recipe fallback, else failed"
on_fail: retry
anti_ban:
delay: [1200, 3000]
- id: a5
step: collect-purchases
verb: ge_collect
target: grand-exchange
params:
note: "collect every filled purchase and refund from the GE collection box into the inventory — staff, candidates/natures, or the recipe's exact inputs (and the equivalent on every restock). Partial offers: the collected portion is added to the ledger; nothing is left in the box at session start"
on_fail: retry
anti_ban:
delay: [600, 1400]
- id: a6
step: equip-staff
verb: menu_option
target: "staff of fire in inventory"
params:
option: "Wield"
note: "Equip requirement is NONE documented on the OSRS wiki Staff of fire page (no requirements row in the infobox, none in the prose; compare Battlestaff 'requires 30 Attack and 30 Magic'; verified 2026-08-28). FAIL-CLOSED path: bounded retries (3), then log ERROR + alert + stop — no fire-rune fallback is modeled (never loop on equip attempts)"
on_fail: retry
anti_ban:
delay: [600, 1800]
- id: a7
step: build-loadout
verb: bank_inventory
target: bank
params:
deposit: [overflow products]
withdraw: "to the tier's carried amounts (only the real bank-stock shortfall — collected purchases from a5 are already in the inventory): LA/HLA 1,000 candidate + 1,000 nature; SII one capacity-sized pack of the selected recipe (steel: 9 iron + 18 coal + 9 natures)"
verify: "the collected staff of fire + tier supplies (a5) are present, and the carried counts EQUAL the tier's carried amounts — the loadout is PROVEN before the first cast (a8/a9); a verified shortfall the bank does not hold sends the slot to bank_supplies instead"
on_fail: retry
anti_ban:
delay: [500, 1100]
- id: a8
step: cast-alch
verb: use_item_on
target: "noted alch candidate in inventory (LA and HLA tiers)"
params:
spell: "Low Level Alchemy (level < 43, or 43-54 when SII is skipped) | High Level Alchemy (level >= 55 — only when the session target is above 55; HLA is never declared-but-unreachable; tier predicates are MUTUALLY EXCLUSIVE — SII only 43 <= Magic < 55, HLA only >= 55 with the target higher, completion first at the target)"
item: "cheapest live alch candidate in inventory — LA/HLA are alchemy tiers, so the selection rule is the alch-candidate rule"
on: "itself"
on_fail: retry
anti_ban:
delay: [1600, 3400] # MANDATORY post-action sleep, ADDITIVE to the 1.8s cast (3 ticks x 0.6s; action-grammar.md:135): cycle = 3.4-5.2s, mid 4.3s -> LA/HLA session-realized ~742 casts/hr incl. startup + restock + breaks (rate_model)
- id: a9
step: cast-superheat
verb: use_item_on
target: "the selected recipe's declared target_input in inventory (the primary ore — never coal; SII tier only, 43 <= Magic < 55)"
params:
spell: "Superheat Item"
selected_recipe: "runtime selection per selection.mode: the recipes-table entry with the highest smithing_level <= current Smithing whose complete input cost at ALLOWED fill prices + one nature rune at allowed fill < the bar's live GE price; fall back to the next lower unlocked recipe when its materials cannot be obtained"
target_input: "the selected recipe's declared target_input field (primary ore; coal can never be the spell target)"
on: "itself"
consumption: "1 x each recipe input (e.g. steel = 1 iron ore + 2 coal) + 1 nature per cast -> 1 bar, +53 Magic xp + the bar's Smithing xp; the pack holds capacity = floor(27 / total input qty) casts (steel = 9)"
on_fail: skip # no unlocked positive-margin recipe -> skip SII, stay on LA (selection.fallback)
anti_ban:
delay: [1600, 3400] # post-action sleep (additive): same 3.4-5.2s/cast as a8; per-recipe realized rates in recipes.rates (steel mid 249.1 casts/hr steady, session anchor 12,400 xp/hr)
- id: a10
step: restock-deposit
verb: bank_inventory
target: bank
params:
deposit: [all coins, all products] # products incl. smelted bars during SII
withdraw: "ALL tier supplies currently in the bank, up to the carried amounts (LA/HLA 1,000/1,000; SII one capacity-sized pack of the selected recipe) — the bank-stock FAST PATH applies only when the bank actually holds enough; the remaining deficit is bought at the GE (a11) and collected (a12)"
on_fail: alert
anti_ban:
delay: [500, 1100]
- id: a11
step: restock-buy
verb: ge_buy
target: grand-exchange
params:
item: "the tier's supplies to top up to the carried amounts"
qty: "deficit = carried amount - inventory count after the a10 fast path (LA/HLA: 901 candidate + 901 natures after a 901-cast batch with an empty bank; SII: the selected recipe's capacity-sized pack minus whatever the bank held)"
max_price: "live low + 5 %"
note: "the deficit is RE-BOUGHT at the GE on every restock — the entry never withdraws a batch that was never purchased; partial/failed fills use the same collect + re-place recovery as the initial acquisition"
on_fail: retry
anti_ban:
delay: [1200, 3000]
- id: a12
step: restock-collect
verb: ge_collect
target: grand-exchange
params:
note: "collect the re-bought deficit into the inventory — carried amounts are restored before casting resumes"
on_fail: retry
anti_ban:
delay: [600, 1400]
- id: a13
step: take-break
verb: take_break
target: "current location"
params:
duration: "[45, 120] seconds of idle AFK per break (matches run_control.break.for)"
note: "executed by the translator when the completed-cast counter reaches run_control.break.every [400, 700] casts — this IS run_control.break's executor, fired on CAST COUNTS, not on bank visits; the cast counter is NOT reset by restocking (breaks land at 550/1,100/1,650 in the midpoint example, independent of the 901-cast alch / capacity-sized SII restock cadences)"
on_fail: skip
run_control:
iteration: "one cast" # schema.md: break.every counts iterations, so every
# value below is in completed casts, NOT bank trips
max_iterations: null
break:
every: [400, 700] # COMPLETED CASTS between AFK breaks (midpoint 550 = ~39 min
# at the 4.3s midpoint cycle). Cast-count-based, independent of
# banking; a restock trip NEVER counts as a break and does not
# reset this counter (owner decision 2026-08-28)
for: [45, 120] # seconds of idle AFK
stop_conditions:
- "magic level >= session target (default 55: completes at the LA/SII -> HLA handoff; when the configured target is above 55, the HLA tier executes from 55 until the target — HLA is never declared-but-unreachable)"
- "session elapsed >= 2 h (session horizon — the rate model amortises startup over it)"
- "total assets (cash + supplies at current buy cost) < the next tier batch at max fill plus staff replacement (LA/HLA: 312,650 = 307,650 + 5,000; SII steel: 10,150 = 5,150 + 5,000) — assets, NOT raw cash: cash that became supplies is not a loss"
- "session net loss = initial capital - total assets > 690,075 — the 6-batch worst-case LA reserve (Magic 21 -> 55 at max fills) is exhausted (a loss is WHEN ASSETS FALL; total assets - initial capital goes NEGATIVE on losses and can never exceed the reserve — the reversed sign never fires)"
- "3 consecutive failed casts (client issue — do not loop)"
- "3 consecutive no-fill GE rounds (initial acquisition or restock) with no fallback recipe/candidate available"
- "staff of fire equip failed after bounded retries — FAIL CLOSED (log ERROR, alert, stop)"
# ================= WORKED EXAMPLE (pinned, deterministic — owner guidance 2026-08-28) =================
# SII tier, steel-bar path (Magic 43-54, Smithing 30, session target 55).
# Constants: break.every [400, 700] casts (the example pins the midpoint
# 550), break.for [45, 120] s; steel capacity = floor(27 / (1 iron + 2 coal))
# = 9 casts per bank cycle — restocks every 9 casts; the counter sequences
# are MONOTONIC and INDEPENDENT (restocking never resets the break counter).
#
# Step 0 — prep (one-time startup ~300-600 s): account has 30 Smithing +
# 43-54 Magic and the LA/HLA-tier capital (>= ~1.31M gp). At the GE bank:
# choose the session target (55); open GE (a2); place offers — staff (a3),
# 1,000 nature runes + LA candidate OR - for the SII window - the selected
# recipe's first pack (steel: 9 iron ore + 18 coal + 9 natures, a4); wait
# for fills (ge_offers_done) and collect everything (a5); equip the staff
# (a6); loadout = 9 iron + 18 coal + 9 natures (a7). Cast counter := 0.
# Steel is the highest UNLOCKED recipe (Smithing 30); bronze (1), iron (15)
# and silver (20) are unlocked but LOWER, so steel is selected. Smithing 20
# accounts select silver-bar (capacity 27) and steel stays locked (never an
# unmet gate).
# Step 1 — casts #1..#9: each cast consumes 1 iron ore + 2 coal + 1 nature
# and produces 1 steel bar (+53 Magic xp, +17.5 Smithing xp). After cast #9
# the inventory holds 9 bars (capacity reached) -> FIRST RESTOCK at cast 9:
# deposit the 9 bars (a10); the bank holds 0 steel inputs (all sales were
# GE buys), so the fast path yields nothing -> open GE (a2), RE-BUY the
# pack deficit 9 iron + 18 coal + 9 natures (a11, live low + 5 %), collect
# (a12) -> inventory rebuilt. Restocks repeat every 9 casts (9, 18, 27, ...,
# and at 558 after the first break below) — capacity-derived, deterministic.
# Step 2 — FIRST SCHEDULED BREAK at cast 550 (midpoint of break.every
# [400, 700]): the 61st cycle is mid-flight — take-break 45-120 s (a13),
# resume at cast 551, finish the cycle (restock at 558). The break fired on
# the CAST COUNTER while a bank cycle was still running: breaks and banking
# are independent by construction, and restocking did NOT shift the break.
# Step 3 — break #2 at cast 1,100 (exactly 550 after #1 — monotonic,
# unaffected by the 9-cast restock cadence); break #3 at 1,650; restocks
# continue at 1,107, 1,116, ... (every 9). On the alch tiers (LA and the
# post-55 HLA tier) the restock cadence is the 901-cast batch: restock #1
# at cast 901, restock #2 at cast 1,802 — monotonic, independent pins just
# like the breaks (restocking never resets or shifts the break counter).
# The SII window ends at Magic 55:
# with the default target 55 the session completes at the LA/SII -> HLA
# handoff; with a target above 55 the HLA tier (a8, 65 xp/cast, 901-cast
# batch model) executes until the target.
# Fallback chain pinned: steel inputs unfillable at the GE (3 failed offers)
# -> drop to silver-bar (20, next lower unlocked) -> if silver is also
# unfillable or margin-negative at allowed fills -> skip SII this window and
# stay on LA throughout 43-54 (a8 handles the SII-skip window explicitly).
# =====================================================================================================
risk:
ban_level: low
notes:
- "3-tick cast cadence is the main detection vector; keep anti_ban delays (cadence pin: additive post-cast sleep [1.6, 3.4]s; session-realized ~742 casts/hr on the alch tiers and ~234 casts/hr on the steel SII anchor, incl. startup + restock + breaks over a 2 h session — loop-only 837 is a ceiling reference, never the output)."
- "Breaks are CAST-COUNT-based (every [400, 700] completed casts, 45-120s AFK) and independent of bank visits — never treat a restock trip as a break, and never reset the break counter on restock (break interval 550 and restock cadences 901/9 are coprime by construction)."
- "The valuable-item warning fires on item GE value (not alch value) — the script must handle the warning widget when alching high-value items (HLA page)."
- "SII: withdraw the EXACT recipe inputs (unnoted, per the recipes table) sized to the derived capacity (floor(27 / input qty)) and cast on the declared target_input (primary ore) — never on coal, never on a noted stack; bank the produced bars when bars_in_inventory >= capacity."
- "SII ore-buy bursts are pace-sensitive like any GE buy; respect the 4h limit window; re-check the allowed-fill complete-cost margin per window because ore/bar prices move independently."
# --- recovery block (owner guidance 2026-08-28: every activity must cover its complete runnable lifecycle) ---
# - Missing materials / depleted supplies: LA/HLA restock at candidate OR nature
# < 100 (carried_batch 1,000/1,000); SII restocks at bars >= the selected
# recipe's capacity (floor(27 / input qty)) or any selected-recipe input < 1 —
# deterministic threshold transitions to bank_supplies; the restock phase
# (a10 -> a2 -> a11 -> a12) RE-BUYS the deficit at the GE — never withdraw a
# batch that was never purchased.
# - GE offers: partial fills are collected (a5/a12) and the remainder re-placed
# at the current edge (a3/a4/a11); after 3 consecutive no-fill rounds (initial
# or restock) the SII recipe falls back to the next lower unlocked recipe;
# with no fallback recipe left, SII is skipped and LA takes over; with no
# fallback candidate, the run stops (failed). Unfilled offers at session end
# are left resting by design (float reserve; coins return when they fill or
# the operator aborts them — the single operator-touch point).
# - Staff of fire cannot be equipped (a6): bounded retries (3), then FAIL
# CLOSED — log ERROR + alert + stop. No fire-rune fallback is modeled (it
# would need its own purchase/supply/rate path; the owner decision rejects a
# prose-only fallback).
# - Invalid loadout (wrong item in hand / spell unavailable): re-run
# build-loadout (a7); 3 consecutive failed casts -> failed (stop, log ERROR
# sentinel, alert).
# - Capital: both capital stops are assets-based (total assets = cash +
# supplies at current buy cost) — cash converted into supplies is not a loss
# and never fires a stop by itself (see stop_conditions).
# - Explicit completion/stop: stop_conditions above (session target 55 or the
# >55 HLA tier, 2 h session horizon, assets floor 312,650 / 10,150, loss
# reserve 690,075, repeated failures, fail-closed staff equip).
sources:
- "OSRS wiki — Low Level Alchemy (21 Magic, 31 xp/cast, members=No, 3-tick, 40% of item value; 'recommended to use High Level Alchemy instead' at 55 Magic)"
- "OSRS wiki — Superheat Item (43 Magic, 53 xp/cast, members=No; smelts ore into bars without a furnace; cannot be cast on noted ore; grants the bar's Smithing XP; requires the bar's Smithing level and materials)"
- "OSRS wiki — High Level Alchemy (55 Magic, 65 xp/cast, members=No, 60% of item value; GE buying for break-even/profit; noted-item casting; valuable-item warning)"
- "OSRS wiki — Smithing (bar smelting table: smithing levels, per-bar XP and ore/coal quantities for bronze/iron/silver/steel/gold/mithril/adamantite/runite)"
- "OSRS wiki — Grand Exchange (offers persist until filled or aborted; filled items and coin refunds are collected from the booth/collection box; partial fills are separately collectable) — verified 2026-08-28"
- "OSRS wiki — Grand Exchange/Buying limits (4-hour window; the limit resets 4 hours after the FIRST unit is bought; outstanding offers remain active and a limit reset is NOT an offer expiry) — verified 2026-08-28"
- "prices.runescape.wiki/api/v1/osrs/mapping (id 561 nature rune: members=false, limit 18000, value 180, lowalch 72; limit cross-checked against Module:Exchange/Nature_rune — agree)"
- "prices.runescape.wiki/api/v1/osrs/latest (nature rune buy 145, snapshot 2026-08-27; steel-bar fixture prices: iron ore 100, coal 150, steel bar 600)"
- "OSRS wiki — Staff of fire (page verified 2026-08-28 for the equip requirement: id 1387, F2P item, tradeable, options Wield — NO requirements row in the Infobox Item and NO requirement statement in the prose; contrast Battlestaff, whose prose states 'requires 30 Attack and 30 Magic to wield')"
- "docs/schema.md run_control (iteration unit; break.every counts iterations) + docs/action-grammar.md:135 (anti_ban.delay = additive post-action sleep) + verbs ge_open/ge_buy/ge_collect/ge_offers_done (separate GE primitives) — the cadence + break + GE + lifecycle model; owner guidance 2026-08-28 (capacity-derived SII, rate_model, configurable target, fail-closed staff); governor comment 10394 (verified build-loadout a7, sign-corrected loss stop)"
verified: "2026-08-27" # keep quoted (YAML 1.1 date parsing); prices last re-fetched live 2026-08-27
related:
- low-alch-f2p
- f2p-alch-catalog
- high-alch-p2p

View File

@ -39,8 +39,10 @@
"item": { "type": "string" },
"item_id": { "type": "integer" },
"cost_per_cycle": { "type": "number" },
"cost_per_cycle_max_fill": { "type": "number" },
"income_per_cycle": { "type": "number" },
"net_per_cycle": { "type": "number" },
"net_per_cycle_max_fill": { "type": "number" },
"gp_per_hour_low": { "type": "number" },
"gp_per_hour_high": { "type": "number" },
"ceiling_gp_per_hour": { "type": "number", "exclusiveMinimum": 0 },
@ -63,6 +65,105 @@
}
},
"selection": {
"type": "object",
"required": ["mode", "eligible_rule", "fallback"],
"additionalProperties": false,
"properties": {
"mode": { "enum": ["highest-unlocked"] },
"eligible_rule": { "type": "string", "minLength": 10 },
"fallback": { "type": "string", "minLength": 10 }
}
},
"recipes": {
"type": "object",
"minProperties": 1,
"propertyNames": { "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$" },
"additionalProperties": {
"type": "object",
"required": ["bar", "smithing_level", "target_input", "inputs"],
"additionalProperties": false,
"properties": {
"bar": { "type": "string", "minLength": 2 },
"bar_item_id": { "type": "integer", "minimum": 1 },
"smithing_level": { "type": "integer", "minimum": 1, "maximum": 126 },
"smithing_xp": { "type": "number", "exclusiveMinimum": 0 },
"target_input": { "type": "string", "minLength": 2 },
"inputs": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": ["item", "qty", "source"],
"additionalProperties": false,
"properties": {
"item": { "type": "string", "minLength": 2 },
"item_id": { "type": "integer", "minimum": 1 },
"qty": { "type": "integer", "minimum": 1 },
"source": { "enum": ["ge", "p2p", "drops", "craft", "bank", "npc-shop"] }
}
}
},
"rates": {
"type": "object",
"required": ["capacity", "casts_per_hour", "magic_xp_per_hour"],
"additionalProperties": false,
"properties": {
"capacity": { "type": "integer", "minimum": 1 },
"casts_per_hour": {
"type": "object",
"required": ["min", "mid", "max"],
"additionalProperties": false,
"properties": {
"min": { "type": "number", "minimum": 0 },
"mid": { "type": "number", "minimum": 0 },
"max": { "type": "number", "minimum": 0 }
}
},
"magic_xp_per_hour": {
"type": "object",
"required": ["min", "mid", "max"],
"additionalProperties": false,
"properties": {
"min": { "type": "number", "minimum": 0 },
"mid": { "type": "number", "minimum": 0 },
"max": { "type": "number", "minimum": 0 }
}
}
}
}
}
}
},
"rate_model": {
"type": "object",
"required": ["anchor_tier", "cast_ticks", "restock_trip_s", "session_horizon_s"],
"additionalProperties": false,
"properties": {
"anchor_tier": { "enum": ["la", "sii", "hla"] },
"anchor_recipe": { "type": "string" },
"cast_ticks": { "type": "integer", "minimum": 1 },
"restock_trip_s": {
"type": "array",
"items": { "type": "number", "minimum": 0 },
"minItems": 2,
"maxItems": 2
},
"session_horizon_s": { "type": "integer", "minimum": 1 },
"startup_s": {
"type": "array",
"items": { "type": "number", "minimum": 0 },
"minItems": 2,
"maxItems": 2
},
"alch_carry_batch": { "type": "integer", "minimum": 1 },
"alch_restock_threshold": { "type": "integer", "minimum": 0 },
"sii_capacity_slots": { "type": "integer", "minimum": 1 }
}
},
"pickaxe_tiers": {
"type": "object",
"minProperties": 1,

File diff suppressed because it is too large Load Diff