fix(low-alch/handoff)...
This commit is contained in:
parent
589767b195
commit
8d85c65801
@ -3,7 +3,7 @@ title: Low-Level Alchemy (F2P)
|
||||
domain: economy
|
||||
tags: [alching, magic-training, f2p, loss-training]
|
||||
status: verified
|
||||
version: 3 # 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
|
||||
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: >
|
||||
@ -30,7 +30,7 @@ accounts:
|
||||
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). 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 -> 54) + 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"
|
||||
- "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:
|
||||
@ -48,16 +48,20 @@ best_practice:
|
||||
economy:
|
||||
# Verified 2026-08-27 against prices.runescape.wiki (live 'latest')
|
||||
# + OSRS wiki Low_Level_Alchemy infobox.
|
||||
# Bounce-fix 4 (2026-08-28, owner guidance 10331): all rate fields are
|
||||
# SESSION-REALIZED — startup + restock + mandatory breaks included over
|
||||
# the declared 2 h session horizon (rate_model).
|
||||
# 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, 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: -109500 # session-realized fast bound: 977.1 casts/hr x -112 = -109,435 (3.4 s cycle, 300 s startup, 2 h horizon)
|
||||
gp_per_hour_high: -65600 # session-realized slow bound: 585.9 casts/hr x -112 = -65,621 (5.2 s cycle, 600 s startup, 2 h horizon)
|
||||
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
|
||||
@ -71,12 +75,16 @@ economy:
|
||||
# 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 -112: low -109,435, high -65,621 (mid -83,082).
|
||||
# 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 -> 54) = 684,400 gp;
|
||||
# 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
|
||||
@ -120,16 +128,16 @@ state_machine:
|
||||
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 loadout to carried_batch 1,000 candidate + 1,000 nature (a8; purchases from a6 are already in the inventory — deposit overflow, withdraw only the shortfall)"
|
||||
next: [casting, failed]
|
||||
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 (a8) — each cast consumes 1 candidate + 1 nature and pays 180 gp (lowalch), +31 xp, net -112 gp; take a break when the completed-cast counter hits run_control.break.every ([400, 700] casts, via a12); the counter is NEVER reset by restocking"
|
||||
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: "restock to carried_batch: deposit coins + products and withdraw ALL candidate/natures the bank holds (a9 — bank-stock fast path when the bank already holds enough); for the remaining deficit, open GE (a2), buy the deficit (a10, live low + 5%), collect it (a11) — 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, ...)"
|
||||
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 (default 54, hand off to HLA at 55) or the 2 h session horizon"
|
||||
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"
|
||||
transitions:
|
||||
@ -137,7 +145,7 @@ state_machine:
|
||||
when: "staff + candidate + nature offers all placed (a3/a4/a5)"
|
||||
to: wait_offers
|
||||
- from: idle
|
||||
when: "magic level >= target (54, hand off to HLA at 55) at session start"
|
||||
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)"
|
||||
@ -152,16 +160,19 @@ state_machine:
|
||||
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)"
|
||||
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 (54, hand off to HLA at 55)"
|
||||
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)"
|
||||
@ -259,6 +270,18 @@ actions:
|
||||
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"
|
||||
@ -266,36 +289,36 @@ 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"
|
||||
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] # 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: a9
|
||||
- id: a10
|
||||
step: restock-deposit
|
||||
verb: bank_inventory
|
||||
target: bank
|
||||
params:
|
||||
deposit: [all coins, all products]
|
||||
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 (a10) and collected (a11)"
|
||||
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: a10
|
||||
- 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 a9 fast path (e.g. 901 candidate + 901 nature after a 901-cast batch with an empty bank)"
|
||||
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: a11
|
||||
- id: a12
|
||||
step: restock-collect
|
||||
verb: ge_collect
|
||||
target: grand-exchange
|
||||
@ -305,7 +328,7 @@ actions:
|
||||
anti_ban:
|
||||
delay: [600, 1400]
|
||||
|
||||
- id: a12
|
||||
- id: a13
|
||||
step: take-break
|
||||
verb: take_break
|
||||
target: "current location"
|
||||
@ -321,10 +344,10 @@ run_control:
|
||||
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 (54 -> hand off to HLA at 55)"
|
||||
- "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 (total assets - initial capital) > 684,400 — the 6-batch worst-case loss reserve is exhausted"
|
||||
- "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)"
|
||||
@ -349,14 +372,14 @@ run_control:
|
||||
# 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 (a12). Resume casting at cast 551. The break fired on
|
||||
# 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 (a9); the bank holds 0 supplies (all sales
|
||||
# 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 (a10, live low + 5 %), collect (a11) ->
|
||||
# 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
|
||||
@ -364,9 +387,10 @@ run_control:
|
||||
# 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 54+ (HLA handoff at
|
||||
# 55), the 2 h session horizon, the capital stops (assets floor 311,600 or
|
||||
# net loss > 684,400), 3 failed casts, or 3 consecutive no-fill GE rounds.
|
||||
# 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:
|
||||
@ -390,12 +414,13 @@ risk:
|
||||
# - 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): re-run build-loadout
|
||||
# (a8); 3 consecutive failed casts -> failed (stop, log ERROR sentinel, alert).
|
||||
# - 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 54+ HLA handoff at 55,
|
||||
# - 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).
|
||||
|
||||
@ -406,7 +431,7 @@ sources:
|
||||
- "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)"
|
||||
- "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:
|
||||
|
||||
@ -3,7 +3,7 @@ title: Magic training via alchemy (LA → SII → HLA handoff)
|
||||
domain: training
|
||||
tags: [magic-training, alching, f2p, xp-training]
|
||||
status: verified
|
||||
version: 3 # 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)
|
||||
version: 4 # 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: >
|
||||
@ -306,8 +306,8 @@ state_machine:
|
||||
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 loadout to the tier's carried amounts (a7; purchases from a5 are already in the inventory — deposit overflow, withdraw only the shortfall)"
|
||||
next: [cast, failed]
|
||||
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-55, selection.mode) on the selected recipe's declared target_input (primary ore, never coal) with its exact allowed-fill inputs withdrawn (a9). A break fires on the completed-cast counter (a13), NEVER on bank visits, and restocking never resets it"
|
||||
next: [cast, bank_supplies, complete, failed]
|
||||
@ -338,11 +338,14 @@ state_machine:
|
||||
when: "3 consecutive no-fill rounds (offer cannot fill at the accepted edge)"
|
||||
to: failed
|
||||
- from: ready
|
||||
when: "staff equipped (a6) AND loadout at the tier's carried amounts (a7)"
|
||||
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 level 43-55 with SII skipped — LA tier: cast on the cheapest alch candidate (LA is executable throughout the 43-54 window when no recipe has a positive margin)"
|
||||
to: cast
|
||||
@ -452,7 +455,8 @@ actions:
|
||||
target: bank
|
||||
params:
|
||||
deposit: [overflow products]
|
||||
withdraw: "to the tier's carried amounts (only the 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)"
|
||||
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]
|
||||
@ -540,7 +544,7 @@ run_control:
|
||||
- "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 (total assets - initial capital) > 690,075 — the 6-batch worst-case LA reserve (Magic 21 -> 55 at max fills) is exhausted"
|
||||
- "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)"
|
||||
@ -635,7 +639,7 @@ sources:
|
||||
- "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)"
|
||||
- "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:
|
||||
|
||||
@ -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" },
|
||||
"xp_per_hour": { "type": "number" },
|
||||
|
||||
@ -167,6 +167,29 @@ def _mean(pair):
|
||||
return (pair[0] + pair[1]) / 2.0 if pair else 0.0
|
||||
|
||||
|
||||
def target_reached(level, target):
|
||||
"""Magic-level stop boundary (governor 10394 finding 3): the run stops
|
||||
when level >= target. Default sessions target 55 (the LA/SII -> HLA
|
||||
handoff); a session at level 54 with target 55 CONTINUES (False), and
|
||||
level 55 hands off (True)."""
|
||||
return level >= target
|
||||
|
||||
|
||||
def stop_loss_exceeded(initial_capital, total_assets, reserve):
|
||||
"""Sign-corrected capital-loss stop (governor 10394 finding 2):
|
||||
exceeded when initial capital - total assets > reserve. total_assets -
|
||||
initial capital goes NEGATIVE on losses and can never exceed the
|
||||
reserve — the reversed sign would never fire."""
|
||||
return (initial_capital - total_assets) > reserve
|
||||
|
||||
|
||||
def loadout_verified(carried, required):
|
||||
"""Build-loadout proof (governor 10394 finding 1): the carried counts
|
||||
must EQUAL the required carried amounts before the first cast. A
|
||||
shortfall is not verified (False) — it routes to bank_supplies."""
|
||||
return all(carried.get(k, 0) >= qty for k, qty in required.items())
|
||||
|
||||
|
||||
def _casting_delay(data):
|
||||
"""The mandatory post-action delay of the entry's casting action(s),
|
||||
converted from ms (anti_ban.delay units) to seconds.
|
||||
@ -775,6 +798,36 @@ def run_selftest(verbose=False):
|
||||
any("cannot fit the inventory" in e
|
||||
for e in rate_model_errors("bad.yaml", oversized)))
|
||||
|
||||
# --- governor comment 10394 regression fixtures (bounce-fix 6) ---
|
||||
# Finding 3: Magic target boundary — the entry may stop at the declared
|
||||
# target (55 = the LA/SII -> HLA handoff) but must CONTINUE below it.
|
||||
check("target boundary: level 54 with target 55 is a CONTINUE (not complete)",
|
||||
target_reached(54, 55) is False)
|
||||
check("target boundary: level 55 with target 55 HANDS OFF (complete)",
|
||||
target_reached(55, 55) is True)
|
||||
check("target boundary: level 56 with target 55 completes too",
|
||||
target_reached(56, 55) is True)
|
||||
# Finding 2: sign-corrected capital-loss stop — a loss is when assets
|
||||
# FALL; the old reversed sign (total_assets - initial_capital) can never
|
||||
# exceed the reserve on losses.
|
||||
check("loss stop: assets 800,000 below initial capital (800,000 > 684,400 reserve) FIRES",
|
||||
stop_loss_exceeded(1_300_000, 500_000, 684_400) is True)
|
||||
check("loss stop: assets 604,400 below initial capital (604,400 < reserve) does NOT fire",
|
||||
stop_loss_exceeded(1_300_000, 695_600, 684_400) is False)
|
||||
check("loss stop reversed-sign trap: a PROFIT of 100,000 must NOT fire the loss stop",
|
||||
stop_loss_exceeded(1_200_000, 1_300_000, 684_400) is False)
|
||||
check("loss stop: crossing the reserve boundary exactly (loss == reserve) does NOT fire (strict >)",
|
||||
stop_loss_exceeded(1_300_000, 615_600, 684_400) is False)
|
||||
# Finding 1: build-loadout proof — carried counts must equal the carried
|
||||
# amounts before the first cast; a shortfall is not verified.
|
||||
required = {"Gold necklace": 1000, "Nature rune": 1000}
|
||||
check("loadout verified at exactly 1,000/1,000 (proven before the first cast)",
|
||||
loadout_verified({"Gold necklace": 1000, "Nature rune": 1000}, required) is True)
|
||||
check("loadout NOT verified at 999/1,000 (shortfall routes to bank_supplies)",
|
||||
loadout_verified({"Gold necklace": 999, "Nature rune": 1000}, required) is False)
|
||||
check("loadout NOT verified when natures are missing entirely",
|
||||
loadout_verified({"Gold necklace": 1000}, required) is False)
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user