codex: low-alch-members — complete GE lifecycle, capital model, monotonic counters, fail-closed staff (bounce-fix attempt 4)

Owner guidance 10325 (governor 10297 findings, exactly):
- GE acquisition path for initial buy AND every restock: open -> place ->
  wait/fill -> collect (ge_open/ge_buy/ge_collect; new wait_offers state),
  partial-fill collect + re-place recovery, bank-stock fast path only when
  the bank actually holds stock — never withdraw an unbought batch.
- Capital model: staff cap 5,000 + first batch at max fill 570,150 +
  6-batch worst-case loss reserve 162,990 + float 570,150 (~1.31M);
  capital stops use TOTAL ASSETS (cash + supplies), never raw cash.
- Monotonic counters: breaks at 550/1,100/1,650 independent of restocks
  at 901/1,802 (gcd 550,901=1); validator regression rejects the old
  1,452 restart bug and enforces the pins.
- Rates: loop-only 837.2 labelled ceiling; steady-state 639-1020 (mid
  791.2); session-realized 586-977 (mid 741.8) over the explicit 2h
  session horizon -> xp 23,000, gp -2,930..-1,760.
- Staff equip FAIL-CLOSED after bounded retries; fire-rune fallback prose
  removed everywhere.
Preserves one-cast iteration, cast-count breaks, rune javelin tips anchor,
pacing/equipment fixes. version 4 -> 5.
This commit is contained in:
Claude 2026-08-28 03:36:45 -05:00
parent 5ddfc6cdbc
commit a197b34aa8
2 changed files with 266 additions and 111 deletions

View File

@ -3,7 +3,7 @@ title: Low-Level Alchemy (Members)
domain: economy domain: economy
tags: [alching, magic-training, members, loss-training] tags: [alching, magic-training, members, loss-training]
status: verified status: verified
version: 4 # 4: bounce-fix 3 2026-08-28 (owner guidance) — iteration = one cast, break.every in CAST counts (independent of bank visits), numeric carried_batch/restock_threshold, realized rates incl. setup+restock+breaks, full-lifecycle actions, pinned worked example version: 5 # 5: bounce-fix 4 2026-08-28 (owner guidance 10325) — complete GE acquisition path (open -> place -> fill -> collect) for initial buy AND every restock (bank-stock fast path only when the bank holds stock); fail-closed staff equip (bounded retries, no fire-rune fallback); capital model (cash vs supplies, staff cost, max-fill worst case, 6-batch loss reserve, float); monotonic break/restock counters (breaks 550/1,100/1,650 independent of restocks 901/1,802) + validator regression; startup-vs-steady-state rates over a 2 h session horizon
updated: "2026-08-28" # ISO dates must stay quoted — YAML 1.1 parses bare dates to datetime.date updated: "2026-08-28" # ISO dates must stay quoted — YAML 1.1 parses bare dates to datetime.date
summary: > summary: >
@ -13,28 +13,35 @@ summary: >
(mapping lowalch != null, members=true, live GE prices 2026-08-27). At (mapping lowalch != null, members=true, live GE prices 2026-08-27). At
current prices the method burns a small amount of GP per cast for 31 XP — current prices the method burns a small amount of GP per cast for 31 XP —
run it for XP, not GP. Least-loss liquid anchor at last check: rune run it for XP, not GP. Least-loss liquid anchor at last check: rune
javelin tips (-3/cast). Model pin (owner guidance 2026-08-28): one javelin tips (-3/cast). Model pins (owner guidance 2026-08-28): one
iteration = one cast; breaks every 400-700 COMPLETED CASTS (cast-count iteration = one cast; breaks every 400-700 COMPLETED CASTS (cast-count
based, independent of bank visits); restock when candidate or nature based, independent of bank visits — midpoint example: 550, 1,100, 1,650,
drops below the numeric restock_threshold (100) against a carried_batch ...); restock when candidate or nature drops below the numeric
of 1,000/1,000 — pinned worked example below. Handoff: switch to High restock_threshold (100) against a carried_batch of 1,000/1,000 (restock
Level Alchemy at level 55 (hard level gate + strictly more coins AND XP cadence 901 casts, monotonic and independent of breaks). EVERY purchase
per cast; see sources). 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; rates are
session-realized (startup + restock + breaks included, see economy).
Handoff: switch to High Level Alchemy at level 55 (hard level gate +
strictly more coins AND XP per cast; see sources).
accounts: accounts:
membership: members membership: members
min_level: 21 # Magic 21 to cast Low Level Alchemy (wiki-verified) min_level: 21 # Magic 21 to cast Low Level Alchemy (wiki-verified)
requires_quest: [] requires_quest: []
requires_other: requires_other:
- "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 — this is the default path)" - "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 — this is the default path). Equip failure is FAIL-CLOSED: flow performs bounded retries (3), then logs ERROR + alerts + stops — there is no fire-rune fallback in this model"
- "A bank with GE access within reach (alch spot = GE bank booth; restock trip 60-120s)" - "A bank with GE access within reach (alch spot = GE bank booth; restock trip [60, 120] s)"
- "Working capital >= 543,000 gp for one carried_batch (1,000 x 403 gp javelin tips + 1,000 x 140 gp natures, live 2026-08-27)" - "Starting capital >= ~1.31M gp CASH (model breakdown: staff buy hard cap 5,000 + first batch at MAX allowed fill 570,150 + session loss reserve 162,990 = 6 worst-case batches at live-low+5% fills (covers Magic 21 -> 55) + resting-offer float 570,150). The capital stop check uses TOTAL ASSETS (cash + supplies at current buy cost), never raw cash — cash legitimately converts into supplies mid-run"
- "30 Attack + 50 Magic ONLY if adopting the optional Bryophyta's staff + Tome of Fire technique — below that the Staff of Fire path applies" - "30 Attack + 50 Magic ONLY if adopting the optional Bryophyta's staff + Tome of Fire technique — below that the Staff of Fire path applies"
best_practice: best_practice:
equipment: equipment:
- item: staff of fire - 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); equip requirement: none documented on the OSRS wiki (verified 2026-08-28)" 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, F2P item, tradeable, no requirements row in the infobox and none in the prose — compare Battlestaff which states 'requires 30 Attack and 30 Magic'); Low Level Alchemy (Cost)" source: "OSRS wiki — Staff of Fire (id 1387, F2P item, tradeable, no requirements row in the infobox and none in the prose — compare Battlestaff which states 'requires 30 Attack and 30 Magic'); Low Level Alchemy (Cost)"
techniques: techniques:
- name: cast on noted (stacked) items - name: cast on noted (stacked) items
@ -52,30 +59,42 @@ economy:
# OSRS wiki (spell infoboxes, Module:Exchange limits, item pages). # OSRS wiki (spell infoboxes, Module:Exchange limits, item pages).
# Re-verified at bounce fix 2026-08-27 with a fresh live fetch; the # Re-verified at bounce fix 2026-08-27 with a fresh live fetch; the
# modeled anchor is the ACTUAL least-loss liquid candidate. # modeled anchor is the ACTUAL least-loss liquid candidate.
# Bounce-fix 3 (2026-08-28, owner guidance): all rate fields are REALIZED # Bounce-fix 4 (2026-08-28, owner guidance 10325): all rate fields are
# figures — setup/restock/breaks included, not just the cast loop. # SESSION-REALIZED figures — startup + restock + mandatory breaks
# included; loop-only and steady-state figures retained as labelled
# references; the 2 h session horizon is explicit (stop_conditions).
item: Rune javelin tips # least-loss liquid anchor 2026-08-27 (see profit_drivers) item: Rune javelin tips # least-loss liquid anchor 2026-08-27 (see profit_drivers)
item_id: 19580 item_id: 19580
cost_per_cycle: 543 # javelin tips buy 403 + nature rune 140 (staff of fire) cost_per_cycle: 543 # javelin tips buy 403 + nature rune 140 (staff of fire, live low 2026-08-27)
income_per_cycle: 540 # lowalch of rune javelin tips (40% of value 1350) income_per_cycle: 540 # lowalch of rune javelin tips (40% of value 1350)
net_per_cycle: -3 net_per_cycle: -3
gp_per_hour_low: -2960 # realized (incl. restock+breaks), fastest cycle 3.4s -> 986.4 casts/hr x -3 = -2,959 gp_per_hour_low: -2930 # session-realized fast bound: 977.2 casts/hr x -3 = -2,932 (3.4 s cycle, 300 s startup, 2 h horizon)
gp_per_hour_high: -1980 # realized (incl. restock+breaks), slowest cycle 5.2s -> 660.6 casts/hr x -3 = -1,982 gp_per_hour_high: -1760 # session-realized slow bound: 585.9 casts/hr x -3 = -1,758 (5.2 s cycle, 600 s startup, 2 h horizon)
xp_per_hour: 24500 # realized midpoint 791.3 casts/hr x 31 xp = 24,530 (pin 24,500) xp_per_hour: 23000 # session-realized midpoint: 741.8 casts/hr x 31 = 22,995 (pin 23,000)
# ---- realized-rate derivation (pinned; owner guidance 2026-08-28) ---- # ---- rate model (pinned; owner guidance 2026-08-28) ----
# LOOP: cycle = 1.8s cast (3 ticks x 0.6s) + anti_ban.delay [1.6, 3.4]s ADDITIVE # LOOP (idealised, zero overhead — ceiling reference ONLY, never the
# (docs/action-grammar.md:135) = 3.4-5.2s, midpoint 4.3s -> loop-only # entry's output): cycle = 1.8 s cast (3 ticks x 0.6 s) +
# casts/hr = 3600/4.3 = 837.2; loop-only xp = 25,953 (837.2 x 31); # anti_ban.delay [1.6, 3.4] s ADDITIVE (docs/action-grammar.md:135)
# loop-only gp midpoint = -2,512 (837.2 x -3). # = 3.4-5.2 s, midpoint 4.3 s -> loop-only casts/hr = 3600/4.3 = 837.2
# OVERHEAD per 901-cast batch (carried_batch 1000 - restock_threshold 100): # (range 692.3..1,058.8); loop-only xp mid = 25,953; gp mid = -2,512.
# restock trip [60, 120]s (midpoint 90s, GE-adjacent booth); # STEADY-STATE (mandatory breaks + restock trips, no startup): per
# breaks: every [400, 700] casts (midpoint 550) for [45, 120]s (midpoint # 901-cast batch: 901 x cycle + restock trip [60, 120] s (mid 90) +
# 82.5s) -> 901/550 = 1.64 breaks x 82.5s = 135s. # break share (break.for mid 82.5 s per break.every mid 550 casts:
# Total overhead = 90 + 135 = 225s per 901 casts. # 45 s/700 at the fast end, 120 s/400 at the slow end):
# REALIZED casts/hr = 901 / ((901 x cycle_s + 225) / 3600): # fast 3.4 s: 901 x 3.4 + 60 + 45 x 901/700 = 3,181.3 s -> 1,019.7 casts/hr
# fast 3.4s -> 986.4; midpoint 4.3s -> 791.3; slow 5.2s -> 660.6. # mid 4.3 s: 901 x 4.3 + 90 + 82.5 x 901/550 = 4,099.5 s -> 791.3 casts/hr
# xp_per_hour = 791.3 x 31 = 24,530 (pin 24,500; range 20,480..30,580). # slow 5.2 s: 901 x 5.2 + 120 + 120 x 901/400 = 5,075.5 s -> 639.2 casts/hr
# gp_per_hour = realized casts/hr x -3: fast -2,959 (low), slow -1,982 (high), midpoint -2,374. # SESSION-REALIZED (2 h horizon = 7,200 s; one-time startup 300-600 s,
# mid 450 s -> 450/7200 = 6.25 %): realized = steady-state x (1 -
# startup/session): fast 977.2, mid 741.8, slow 585.9 casts/hr.
# xp mid = 741.8 x 31 = 22,995 -> pin 23,000; gp = casts/hr x -3:
# low -2,932 (fast), high -1,758 (slow), mid -2,225.
# BATCH ECONOMICS (901 casts; deposits 901 x 540 = 486,540 gp per batch):
# input cost at live low: 901 x 543 = 489,243 -> net -2,703/batch
# input cost at MAX fill (live low + 5 %): 901 x 570.15 = 513,705 -> net
# -27,165/batch (worst case). Reserve = 6 worst-case batches (Magic
# 21 -> 55 = ~5,300 casts = ~6 batches) = 162,990 gp; the capital stop
# checks TOTAL ASSETS, not raw cash (see accounts + stop_conditions).
supplies: supplies:
- item: rune javelin tips - item: rune javelin tips
qty_per_cycle: 1 qty_per_cycle: 1
@ -101,37 +120,58 @@ economy:
- "9. Adamant spear(p) (id 1259, lowalch 832, buy 450, margin +382, GE limit 125)" - "9. Adamant spear(p) (id 1259, lowalch 832, buy 450, margin +382, GE limit 125)"
- "10. Mystic mist staff (id 20733, lowalch 17000, buy 75727, margin -58727, GE limit 8) — buy spiked ABOVE alch value 2026-08-27; a true loss trap, do not buy" - "10. Mystic mist staff (id 20733, lowalch 17000, buy 75727, margin -58727, GE limit 8) — buy spiked ABOVE alch value 2026-08-27; a true loss trap, do not buy"
- "Caveat: thin items (limits 5-125) cannot sustain a run and their 'low' fills can be stale — a bot cannot reliably buy at those prices. The dependable loss-minimizers are the liquid high-limit pair modeled above: rune javelin tips (-3/cast) and rune arrow (-25/cast)." - "Caveat: thin items (limits 5-125) cannot sustain a run and their 'low' fills can be stale — a bot cannot reliably buy at those prices. The dependable loss-minimizers are the liquid high-limit pair modeled above: rune javelin tips (-3/cast) and rune arrow (-25/cast)."
- "Batch-scale economics (default staff-of-fire path, worst case 1 nature/cast): carried_batch 1,000/1,000, restock every 901 casts — one batch = -2,703 gp, +27,931 xp, 2 x 540 gp coin deposits." - "Batch-scale economics (default staff-of-fire path, worst case 1 nature/cast): carried_batch 1,000/1,000, restock every 901 casts — one batch = -2,703 gp at live-low fills (-27,165 at MAX allowed fills), +27,931 xp, deposits 486,540 gp; unfilled-offer float (up to 570,150 gp) is separate from loss (see accounts)."
state_machine: state_machine:
initial: idle initial: idle
terminal: [complete, failed] terminal: [complete, failed]
states: states:
idle: idle:
goal: "acquire + equip the staff of fire, GE-buy candidate + natures, build the starting inventory to carried_batch (1,000 candidate + 1,000 nature)" 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: [casting, complete, failed] 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 (a3/a4/a5); 3 consecutive no-fill rounds (offer cannot fill at the accepted edge) -> failed. An offer that is still unfilled at session end is left resting by design — its coins are tied up (float reserve) and return when it fills or is aborted by the operator (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 loadout to carried_batch 1,000 candidate + 1,000 nature (a8; purchases from a6 are already in the inventory; a8 deposits overflow and confirms the counts)"
next: [casting, failed]
casting: casting:
goal: "cast Low Level Alchemy on the current candidate item — each cast consumes 1 candidate + 1 nature and pays 540 gp (lowalch), +31 xp, net -3 gp; take a break when the completed-cast counter hits run_control.break.every ([400, 700] casts)" goal: "cast Low Level Alchemy on the candidate (a9) — each cast consumes 1 candidate + 1 nature and pays 540 gp (lowalch), +31 xp, net -3 gp; 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] next: [bank_supplies, complete, failed]
bank_supplies: bank_supplies:
goal: "deposit coins + products, withdraw candidate + natures back to carried_batch (1,000/1,000), resume casting" 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, failed] next: [casting, bank_supplies, failed]
complete: complete:
goal: "reached the run's XP/level target (default 55 -> hand off to HLA)" goal: "reached the run's XP/level target (default 55 -> hand off to HLA) or the 2 h session horizon"
failed: failed:
goal: "hard failure — stop, log ERROR sentinel, alert" goal: "hard failure — stop, log ERROR sentinel, alert"
transitions: transitions:
- from: idle - from: idle
when: "staff equipped AND candidate == carried_batch AND nature == carried_batch (1,000 and 1,000)" when: "staff + candidate + nature offers all placed (a3/a4/a5)"
to: casting to: wait_offers
- from: idle - from: idle
when: "magic level >= target (55, hand off to HLA)" when: "magic level >= target (55) at session start"
to: complete to: complete
- from: idle - from: idle
when: "3 consecutive purchase/equip failures with no fallback available (recovery block below)" 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 (a3/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 loadout at carried_batch 1,000/1,000 (a8)"
to: casting
- from: ready
when: "staff equip failed after bounded retries — FAIL CLOSED"
to: failed to: failed
- from: casting - from: casting
when: "candidate count < restock_threshold (100) OR nature rune count < restock_threshold (100) — deterministic restock trigger, pinned numbers" when: "candidate count < restock_threshold (100) OR nature count < restock_threshold (100) — deterministic restock trigger, pinned numbers"
to: bank_supplies to: bank_supplies
- from: casting - from: casting
when: "magic level >= target (55, hand off to HLA)" when: "magic level >= target (55, hand off to HLA)"
@ -140,8 +180,14 @@ state_machine:
when: "3 consecutive failed casts (client issue — do not loop)" when: "3 consecutive failed casts (client issue — do not loop)"
to: failed to: failed
- from: bank_supplies - from: bank_supplies
when: "restocked to carried_batch (1,000 candidate + 1,000 nature)" when: "restock complete: candidate == carried_batch (1,000) AND nature == carried_batch (1,000) — bank fast path or re-bought deficit collected"
to: casting 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
# --- handoff note (wiki-verified 2026-08-27, do not copy from other entries) --- # --- handoff note (wiki-verified 2026-08-27, do not copy from other entries) ---
# Low Level Alchemy: level 21, 31 xp/cast, 1 nature + 3 fire (spellbook: Normal). # Low Level Alchemy: level 21, 31 xp/cast, 1 nature + 3 fire (spellbook: Normal).
@ -171,27 +217,27 @@ actions:
delay: [400, 1600] delay: [400, 1600]
- id: a2 - 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/a5/a11) or collects (a6/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 step: buy-staff
verb: ge_buy verb: ge_buy
target: grand-exchange target: grand-exchange
params: params:
item: "staff of fire" item: "staff of fire"
qty: 1 qty: 1
max_price: "live low + 25% (wiki value 1,500; hard cap 5,000)" max_price: "live low + 25 % (wiki value 1,500; hard cap 5,000)"
on_fail: retry # retry once; on a second failure use the fire-rune fallback (see a3 note / recovery block) 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)"
anti_ban:
delay: [1200, 3000]
- id: a3
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 which states 'requires 30 Attack and 30 Magic'; verified 2026-08-28). FAILURE PATH — if Wield is unavailable or the staff cannot be equipped, fall back to buying 3 fire runes per cast (3 fire + 1 nature = 155 gp rune cost vs 140, net -18/cast at the anchor), log WARN and continue; never loop on equip attempts."
on_fail: retry on_fail: retry
anti_ban: anti_ban:
delay: [600, 1800] delay: [1200, 3000]
- id: a4 - id: a4
step: buy-supplies step: buy-supplies
@ -200,8 +246,9 @@ actions:
params: params:
item: "current least-loss live candidate (default: rune javelin tips)" item: "current least-loss live candidate (default: rune javelin tips)"
qty: "carried_batch = 1,000 (fits the 4h GE limit window of 10,000)" qty: "carried_batch = 1,000 (fits the 4h GE limit window of 10,000)"
max_price: "live low + 5%" max_price: "live low + 5 %"
on_fail: retry # re-place at live low +5% with paced offers; 3 consecutive failures -> failed (recovery block) 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: anti_ban:
delay: [1200, 3000] delay: [1200, 3000]
@ -212,21 +259,45 @@ actions:
params: params:
item: "nature rune" item: "nature rune"
qty: "carried_batch = 1,000 (4h GE limit 18,000)" qty: "carried_batch = 1,000 (4h GE limit 18,000)"
max_price: "live low + 5%" max_price: "live low + 5 %"
note: "offer rests until filled or the run ends; partial fills handled like a4"
on_fail: retry on_fail: retry
anti_ban: anti_ban:
delay: [1200, 3000] delay: [1200, 3000]
- id: a6 - 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 which states '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 step: build-loadout
verb: bank_inventory verb: bank_inventory
target: bank target: bank
params: params:
deposit: [overflow products] deposit: [overflow products]
withdraw: "candidate 1,000 + nature runes 1,000 (= carried_batch; both stackable, two inventory slots)" withdraw: "to carried_batch: candidate 1,000 + nature runes 1,000 (only the shortfall — collected purchases from a6 are already in the inventory; both stackable, two inventory slots)"
on_fail: retry on_fail: retry
anti_ban:
delay: [500, 1100]
- id: a7 - id: a9
step: cast-alch step: cast-alch
verb: use_item_on verb: use_item_on
target: "candidate item in inventory" target: "candidate item in inventory"
@ -237,24 +308,49 @@ actions:
consumption: "1 candidate + 1 nature per cast (staff of fire default); pays 540 gp (lowalch) + 31 xp, net -3 gp" consumption: "1 candidate + 1 nature per cast (staff of fire default); pays 540 gp (lowalch) + 31 xp, net -3 gp"
on_fail: retry on_fail: retry
anti_ban: 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 = 1.8s + 1.6-3.4s = 3.4-5.2s, midpoint 4.3s -> realized ~791 casts/hr incl. restock+breaks (pinned in economy) 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 (pinned in economy)
- id: a8 - id: a10
step: restock step: restock-deposit
verb: bank_inventory verb: bank_inventory
target: bank target: bank
params: params:
deposit: [all coins, all products] deposit: [all coins, all products]
withdraw: "candidate + nature runes up to carried_batch (1,000/1,000)" 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 on_fail: alert
anti_ban:
delay: [500, 1100]
- id: a9 - 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 step: take-break
verb: take_break verb: take_break
target: "current location" target: "current location"
params: params:
duration: "[45, 120] seconds of idle AFK per break (matches run_control.break.for)" 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" 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 on_fail: skip
run_control: run_control:
@ -265,63 +361,85 @@ run_control:
for: [45, 120] # seconds of idle AFK per break for: [45, 120] # seconds of idle AFK per break
stop_conditions: stop_conditions:
- "magic level >= target (55 -> hand off to HLA)" - "magic level >= target (55 -> hand off to HLA)"
- "gp under floor (working capital below one carried_batch = 543,000 gp)" - "session elapsed >= 2 h (session horizon — startup is amortised over this window in the rate model)"
- "total assets (cash + supplies at current buy cost) < 575,150 — cannot fund the next batch at max fill (570,150) plus staff replacement (5,000); assets, NOT raw cash: cash that became supplies is not a loss"
- "session net loss (total assets - initial capital) > 162,990 — the 6-batch worst-case loss reserve is exhausted"
- "3 consecutive failed casts (client issue — do not loop)" - "3 consecutive failed casts (client issue — do not loop)"
- "3 consecutive failed GE purchases with no fallback candidate available" - "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) ================= # ================= WORKED EXAMPLE (pinned, deterministic — owner guidance 2026-08-28) =================
# Constants: carried_batch = 1,000 candidate + 1,000 nature; restock_threshold = 100; # Constants: carried_batch = 1,000 candidate + 1,000 nature; restock_threshold = 100;
# break.every = [400, 700] casts (midpoint 550); break.for = [45, 120] s; # break.every = [400, 700] casts — the example pins the MIDPOINT 550;
# default staff-of-fire path consumes 1 candidate + 1 nature per cast (worst case 1:1 — # 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 —
# with Bryophyta's staff the nature side drains slower, so the candidate stays binding). # with Bryophyta's staff the nature side drains slower, so the candidate stays binding).
# 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 (validator regression: the old buggy
# 'restart the interval at the restock' example is rejected by the gate).
# #
# Step 0 — before the first inventory: account holds >= 543,000 gp working capital # Step 0 — preparation (one-time startup, ~300-600 s): account holds >= ~1.31M gp
# (1,000 x 403 + 1,000 x 140). At the GE bank: GE-buy staff of fire, Wield it # cash (staff cap 5,000 + first batch max 570,150 + reserve 162,990 + float
# (no documented requirement), GE-buy 1,000 rune javelin tips + 1,000 nature runes, # 570,150). At the GE bank: open GE (a2); place offers — staff (a3), 1,000
# withdraw both -> loadout = carried_batch 1,000/1,000. Cast counter := 0. # rune javelin tips (a4), 1,000 nature runes (a5); wait for fills
# Step 1 — casts #1..#550: each cast consumes 1 candidate + 1 nature (-543 gp) and pays # (ge_offers_done) and collect everything (a6); equip the staff (a7); loadout
# 540 gp (lowalch) + 31 xp -> net -3 gp. Cast counter 0 -> 550 (~39 min at midpoint). # = carried_batch 1,000/1,000 (a8). Cast counter := 0; cash ledger opened.
# Step 2 — FIRST SCHEDULED BREAK at cast 550 (midpoint of break.every [400, 700]): run # Step 1 — casts #1..#550: each cast consumes 1 candidate + 1 nature (-543 gp)
# take-break for 45-120 s. Resume casting at cast 551. The break fired on the CAST # and pays 540 gp (lowalch) + 31 xp -> net -3 gp. Cast counter 0 -> 550
# COUNT, not on any bank visit; the restock counter is untouched. # (~39 min at midpoint).
# Step 3 — casts #551..#901: countdown candidate 1000-901 = 99 < 100 (restock_threshold) # Step 2 — FIRST SCHEDULED BREAK at cast 550 (the interval since cast 0):
# and nature 1000-901 = 99 < 100 simultaneously (worst case 1:1) -> the transition # take-break 45-120 s (a13). Resume casting at cast 551. The break fired on
# fires at the end of cast #901: FIRST RESTOCK/BANK EVENT at cast 901. # the CAST COUNT, not on any bank visit.
# Coins deposited at this restock: 901 x 540 = 486,540 gp; batch net so far -2,703 gp; # Step 3 — casts #551..#901: countdown candidate 1,000-901 = 99 < 100
# batch xp so far 901 x 31 = 27,931. # (restock_threshold) and nature 99 < 100 simultaneously (worst case 1:1) ->
# Step 4 — restock: deposit coins + products, withdraw 901 candidate + 901 nature -> # the transition fires at the end of cast #901: FIRST RESTOCK at cast 901.
# carried_batch restored to 1,000/1,000. Resume casting at cast 902 (cast counter # Deposit 901 x 540 = 486,540 gp (a10); the bank holds 0 supplies (all sales
# continues; the break counter also continues — restocking never resets either). # were GE buys), so the fast path yields nothing -> open GE (a2), RE-BUY the
# Step 5 — SECOND BREAK when the cast-count interval since break #1 completes # deficit 901 candidate + 901 nature (a11, live low + 5 %), collect (a12) ->
# ([400, 700], midpoint: cast 902 + 550 = 1,452): take-break, resume at 1,453. # inventory back to 1,000/1,000. Batch net: -2,703 gp (live-low fills) or
# Step 6 — restock #2 fires at cast 1,802 (901-cast batch cadence), and so on, until a # -27,165 gp (max fills). The cast counter (901) and break counter continue.
# stop_condition fires (Magic >= 55 -> HLA handoff; gp under floor; 3 failed casts). # Step 4 — SECOND BREAK at cast 1,100: the interval since break #1 is exactly
# Bank visits and breaks are INDEPENDENT by construction: restock cadence = 901 casts; # 550 casts (1,100 - 550); the cast-901 restock did NOT reset or shift it.
# break cadence = 400-700 casts, and the two counters do not interact. # 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 (HLA handoff),
# the 2 h session horizon, the capital stops (assets floor 575,150 or net
# loss > 162,990), 3 failed casts, or 3 consecutive no-fill GE rounds.
# ============================================================================================ # ============================================================================================
risk: risk:
ban_level: low ban_level: low
notes: notes:
- "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; realized ~791 casts/hr at the 4.3s midpoint cycle incl. restock + breaks; loop-only 837)." - "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." - "Breaks are CAST-COUNT-based (every [400, 700] completed casts, 45-120 s AFK) and independent of bank visits — never treat a restock trip as a break, and never reset the break counter on restock (break/restock gcd(550, 901) = 1)."
- "GE buy bursts: pace offers, respect the 4h limit window (10,000 javelin tips / 18,000 natures); thin items (limit < 100) cannot sustain a long run." - "GE buy bursts: pace offers, respect the 4h limit window (10,000 javelin tips / 18,000 natures); thin items (limit < 100) cannot sustain a long run; offers persist until filled or aborted and a 4h limit reset is NOT an offer expiry."
- "Thin-item 'latest low' fills can be stale — confirm fill price with a live GE offer before committing a run; the liquid high-limit pair (rune javelin tips / rune arrow) is the dependable loss-minimizer." - "Thin-item 'latest low' fills can be stale — confirm fill price with a live GE offer before committing a run; the liquid high-limit pair (rune javelin tips / rune arrow) is the dependable loss-minimizer."
# --- recovery block (owner guidance 2026-08-28: every activity must cover its complete runnable lifecycle) --- # --- recovery block (owner guidance 2026-08-28: every activity must cover its complete runnable lifecycle) ---
# - Missing materials / depleted supplies: the restock_threshold transition (casting -> # - Missing materials / depleted supplies: the restock_threshold transition (casting ->
# bank_supplies at candidate OR nature < 100) catches both mid-run depletions; restock # bank_supplies at candidate OR nature < 100) catches both mid-run depletions; the
# action a8 rebuilds to carried_batch and the loop resumes (worked example steps 3-4). # restock phase (a10 -> a2 -> a11 -> a12) rebuilds to carried_batch and the loop
# - Failed GE purchases (offer unfilled): re-place at live low + 5% with paced offers # resumes (worked example steps 3-4). The entry NEVER withdraws a batch that was
# (a4/a5); after 3 consecutive failures, alert + stop (failed) unless a fallback # never purchased — every restock re-buys the deficit at the GE.
# candidate is switched in — never sit in an unfillable offer loop. # - GE offers: partial fills are collected (a6/a12) and the remainder is re-placed at
# - Staff of fire cannot be equipped (a3): fall back to buying 3 fire runes per cast # the current edge (a3/a4/a5/a11); after 3 consecutive no-fill rounds (initial or
# (3 fire + 1 nature = 155 gp/cast rune cost vs 140; net -18/cast at the anchor), # restock) the run stops (failed) — never sit in an unfillable offer loop. Unfilled
# log WARN, continue; never loop on equip attempts. # offers at session end are left resting by design (float reserve; coins return when
# - Invalid loadout (wrong item in hand / spell unavailable): re-run build-loadout (a6); # they fill or the operator aborts them — the single operator-touch point).
# 3 consecutive failed casts -> failed (stop, log ERROR sentinel, alert). # - Staff of fire cannot be equipped (a7): bounded retries (3), then FAIL CLOSED —
# - Explicit completion/stop: stop_conditions above (Magic 55 HLA handoff, gp floor, # log ERROR + alert + stop. No fire-rune fallback is modeled (it would need its own
# repeated failures). # purchase/supply/rate path; the owner decision rejects a prose-only fallback).
# - Invalid loadout (wrong item in hand / spell unavailable): re-run build-loadout
# (a8); 3 consecutive failed casts -> failed (stop, log ERROR sentinel, alert).
# - 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 HLA handoff, 2 h session
# horizon, assets floor 575,150, loss reserve 162,990, repeated failures, fail-closed
# staff equip).
sources: sources:
- "OSRS wiki — Low Level Alchemy (level 21, 31 xp/cast, 3-tick, 1 nature + 3 fire; spellbook Normal; infobox 2026-08-27)" - "OSRS wiki — Low Level Alchemy (level 21, 31 xp/cast, 3-tick, 1 nature + 3 fire; spellbook Normal; infobox 2026-08-27)"
@ -332,12 +450,14 @@ sources:
- "prices.runescape.wiki latest 2026-08-27, re-fetched at bounce fix (low = per-repo buy basis: rune javelin tips 403, rune arrow 45, nature rune 140, jade ring 303, opal amulet (u) 17)" - "prices.runescape.wiki latest 2026-08-27, re-fetched at bounce fix (low = per-repo buy basis: rune javelin tips 403, rune arrow 45, nature rune 140, jade ring 303, opal amulet (u) 17)"
- "OSRS wiki — Module:Exchange/Rune javelin tips (limit 10000, lowalch 540, highalch 810, members=true — matches mapping; re-verified 2026-08-27) + Module:Exchange/Nature rune, Staff of fire and all cited candidates (GE limits agree with mapping; verified 2026-08-27)" - "OSRS wiki — Module:Exchange/Rune javelin tips (limit 10000, lowalch 540, highalch 810, members=true — matches mapping; re-verified 2026-08-27) + Module:Exchange/Nature rune, Staff of fire and all cited candidates (GE limits agree with mapping; verified 2026-08-27)"
- "OSRS wiki — Rune javelin tips / Rune arrow / Bagged flower / Mystic mist staff etc. item pages (members = Yes for every cited catalog item; verified 2026-08-27)" - "OSRS wiki — Rune javelin tips / Rune arrow / Bagged flower / Mystic mist staff etc. item pages (members = Yes for every cited catalog item; verified 2026-08-27)"
- "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 — Nature rune (members craft at level 44 Runecraft from pure essence, 9 xp; Nature Altar south-east of Tai Bwo Wannai)" - "OSRS wiki — Nature rune (members craft at level 44 Runecraft from pure essence, 9 xp; Nature Altar south-east of Tai Bwo Wannai)"
- "OSRS wiki — Abyss (access to runic altars without talismans via the Mage of Zamorak)" - "OSRS wiki — Abyss (access to runic altars without talismans via the Mage of Zamorak)"
- "OSRS wiki — Bryophyta's staff (level 30 Attack + Magic; up to 1,000 nature charges; 1/15 chance to not consume a charge on any nature-rune spell; alchemy combo with Tome of Fire)" - "OSRS wiki — Bryophyta's staff (level 30 Attack + Magic; up to 1,000 nature charges; 1/15 chance to not consume a charge on any nature-rune spell; alchemy combo with Tome of Fire)"
- "OSRS wiki — Tome of Fire (members, level 50 Magic to wield; charged tome is an infinite fire-rune source and non-combat spells do not consume charges)" - "OSRS wiki — Tome of Fire (members, level 50 Magic to wield; charged tome is an infinite fire-rune source and non-combat spells do not consume charges)"
- "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')" - "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) + verb take_break — the cadence + break + recovery model" - "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, never assumed) — the cadence + break + GE + recovery model"
verified: "2026-08-27" # keep quoted (YAML 1.1 date parsing); prices last re-fetched live 2026-08-27 verified: "2026-08-27" # keep quoted (YAML 1.1 date parsing); prices last re-fetched live 2026-08-27
related: related:

View File

@ -41,6 +41,37 @@ def load_entries():
return sorted(out) return sorted(out)
def alch_worked_example_regression(rel, data, raw_text):
"""One-cast model worked-example regression (owner guidance 2026-08-28,
PR #8 repair): entries with the owner-selected one-cast iteration model
must pin a worked example with MONOTONIC, INDEPENDENT counters — breaks
every 550 casts (1,100, 1,650, ...) regardless of restocks, and restocks
every 901 casts (1,802, ...), so restocking never resets or shifts the
break counter. The old 'restart the interval at the restock' bug (break
#2 at cast 1,452 = 901 + restarted 549) must never reappear. Scoped to
entries that declare the one-cast model AND carry a worked example, so
unrelated entries are untouched."""
ruco = data.get("run_control") or {}
if "one cast" not in str(ruco.get("iteration", "")):
return []
if "worked example" not in raw_text.lower():
return []
errs = []
if "1,452" in raw_text:
errs.append(f"{rel}: worked example resets the break interval at a restock "
f"(cast 1,452 = 901 + restarted interval) — breaks must stay "
f"monotonic at 550/1,100/1,650 independently of restocks")
for marker, label in (("550", "break #1 (cast 550)"),
("901", "restock #1 (cast 901)"),
("1,100", "break #2 (cast 1,100)"),
("1,650", "break #3 (cast 1,650)"),
("1,802", "restock #2 (cast 1,802)")):
if marker not in raw_text:
errs.append(f"{rel}: worked example missing monotonic counter pin "
f"{label} — breaks/restocks must be pinned literally")
return errs
def main(): def main():
global STALE_DAYS global STALE_DAYS
if "--stale-days" in sys.argv: if "--stale-days" in sys.argv:
@ -99,6 +130,10 @@ def main():
if term not in declared: if term not in declared:
errors.append(f"{rel}: terminal '{term}' not declared") errors.append(f"{rel}: terminal '{term}' not declared")
# one-cast worked-example regression (owner guidance 2026-08-28):
# monotonic break/restock counters, scoped to the one-cast model
errors += alch_worked_example_regression(rel, data, path.read_text())
# action id/step uniqueness # action id/step uniqueness
a_ids = [a["id"] for a in data["actions"]] a_ids = [a["id"] for a in data["actions"]]
a_steps = [a["step"] for a in data["actions"]] a_steps = [a["step"] for a in data["actions"]]