codex: low-alch-members — loss-reserve stop direction + fill-price scenario GP bounds (bounce-fix attempt 5)

Owner guidance 2026-08-29 for governor review 10361 (PR #8), finding by finding:

1. Loss-reserve stop direction — the stop read 'session net loss (total
   assets - initial capital) > 162,990'; a loss makes that LHS negative so
   it could never exceed the positive reserve (the stop could never fire).
   Now expressed everywhere as 'initial capital - total assets > 162,990'
   (stop_conditions, worked-example step 6, recovery block, version pin):
   a loss makes the difference positive, the strict-greater comparison
   fires on losses and never on profits, and exactly-at-boundary does not
   fire. tools/validate.py gains alch_accounting_regression(): comparator
   boundary fixtures (below 162,989 / at 162,990 / above 162,991 / profit
   side +50,000 / breakeven) plus entry-text pins rejecting the reversed
   form anywhere in the file.

2. Fill-price scenarios for realized GP bounds — previously gp_per_hour_low
   /high derived only from the live-low -3.00/cast margin while the buy
   policy permits live low + 5% (candidate 423.15 + nature 147 = 570.15 vs
   540 lowalch = -30.15/cast). The economy block now declares FILL-PRICE
   SCENARIOS A (live-low 543 -> -3.00/cast) and B (max allowed +5% 570.15
   -> -30.15/cast) as the single ledger behind capital, batch economics
   AND the GP bounds: gp_per_hour_low -29,463 (scenario B x session-fast
   977.2), gp_per_hour_high -1,758 (scenario A x session-slow 585.9),
   midpoints -22,365 / -2,225 pinned; validator enforces the exact bound
   values and the -30.15/570.15/423.15 scenario pins.

Preserved: complete GE acquisition/restock flow, one-cast iteration,
monotonic break/restock counters, total-assets accounting (floor
575,150), fail-closed staff path, and the earlier fixed pacing/equip
findings. version 5 -> 6, updated 2026-08-29.

Gate: python3 tools/validate.py -> 2 entries, 0 errors, 1 pre-existing
F2P WARN (low-alch-f2p researched, untouched); git diff --check clean;
regression negative-tested (old gp pins, reversed form, missing scenario
pins all rejected).
This commit is contained in:
Claude 2026-08-29 03:00:56 -05:00
parent a197b34aa8
commit 052c6984ac
2 changed files with 110 additions and 11 deletions

View File

@ -3,8 +3,8 @@ title: Low-Level Alchemy (Members)
domain: economy
tags: [alching, magic-training, members, loss-training]
status: verified
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
version: 6 # 6: bounce-fix 5 2026-08-29 (owner guidance for governor 10361) — loss-reserve stop fixed to 'initial capital - total assets > 162,990' everywhere + validator boundary fixtures (below/at/above/profit) + realized GP/hr bounds derived from declared fill-price scenarios (A live-low -3.00/cast, B max allowed +5% -30.15/cast): gp low -29,463 / high -1,758. # 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-29" # ISO dates must stay quoted — YAML 1.1 parses bare dates to datetime.date
summary: >
Magic training from level 21 by low-alching members items with the lowest
@ -13,7 +13,10 @@ summary: >
(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 —
run it for XP, not GP. Least-loss liquid anchor at last check: rune
javelin tips (-3/cast). Model pins (owner guidance 2026-08-28): one
javelin tips (live-low net -3/cast; realized loss band -1,758 to -29,463
gp/hr across the declared live-low and max-allowed +5% fill scenarios —
XP is fill-independent at 23,000/hr session-realized midpoint). 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
@ -68,8 +71,8 @@ economy:
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)
net_per_cycle: -3
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: -1760 # session-realized slow bound: 585.9 casts/hr x -3 = -1,758 (5.2 s cycle, 600 s startup, 2 h horizon)
gp_per_hour_low: -29463 # WORST-CASE realized: max-allowed fill scenario B (-30.15/cast) x session-fast 977.2 casts/hr = -29,462.6 (3.4 s cycle, 300 s startup, 2 h horizon) — see FILL-PRICE SCENARIOS below
gp_per_hour_high: -1758 # BEST-CASE realized: live-low fill scenario A (-3.00/cast) x session-slow 585.9 casts/hr = -1,757.7 (5.2 s cycle, 600 s startup, 2 h horizon) — see FILL-PRICE SCENARIOS below
xp_per_hour: 23000 # session-realized midpoint: 741.8 casts/hr x 31 = 22,995 (pin 23,000)
# ---- rate model (pinned; owner guidance 2026-08-28) ----
# LOOP (idealised, zero overhead — ceiling reference ONLY, never the
@ -87,14 +90,30 @@ economy:
# 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.
# xp mid = 741.8 x 31 = 22,995 -> pin 23,000. REALIZED GP/hr derives from
# the FILL-PRICE SCENARIOS below (the SAME ledger as the batch economics):
# worst = -29,463 = scenario B x session-fast 977.2 (-30.15 x 977.2 = -29,462.6)
# best = -1,758 = scenario A x session-slow 585.9 (-3.00 x 585.9 = -1,757.7)
# midpoints: scenario A x 741.8 = -2,225; scenario B x 741.8 = -22,365.
# 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).
# FILL-PRICE SCENARIOS (one ledger for capital, batch economics, AND the
# realized GP bounds — every number below comes from the same scenario):
# A live-low fill: candidate 403 + nature 140 = 543/cast vs
# 540 lowalch -> net -3.00/cast (the anchor)
# B max allowed fill (+5%): candidate 403x1.05 = 423.15 + nature
# 140x1.05 = 147 -> 570.15/cast vs 540 lowalch
# -> net -30.15/cast (the executable buy policy
# permits live low + 5%; this scenario, not the
# -3 live-low margin, bounds the downside)
# gp_per_hour_low = -29,463 | gp_per_hour_high = -1,758 derive from these
# scenarios at the session rates above; the batch ledger uses the same
# 543 / 570.15 per-cast costs (901-cast batch: -2,703 live-low, -27,165
# max fill), so capital accounting and GP bounds can never diverge.
supplies:
- item: rune javelin tips
qty_per_cycle: 1
@ -363,7 +382,7 @@ run_control:
- "magic level >= target (55 -> hand off to HLA)"
- "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"
- "session net loss: initial capital - total assets > 162,990 — a loss makes 'initial capital - total assets' POSITIVE, so this strict-greater comparison fires on losses and never on profits; 162,990 = the 6-batch worst-case reserve (exactly AT the boundary it does NOT fire — strict >)"
- "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)"
@ -406,8 +425,9 @@ run_control:
# 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.
# the 2 h session horizon, the capital stops (assets floor 575,150, or
# loss = initial capital - total assets > 162,990), 3 failed casts, or 3
# consecutive no-fill GE rounds.
# ============================================================================================
risk:
@ -436,7 +456,11 @@ risk:
# (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.
# supplies is not a loss and never fires a stop by itself. Loss direction is pinned
# everywhere as 'initial capital - total assets > 162,990': a loss makes that
# difference positive, so the stop fires on losses and never on profits (the
# reversed sign form can never exceed the threshold on a loss and is rejected by
# the validator regression; boundary fixtures: below/at/above + profit side).
# - 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).

View File

@ -72,6 +72,77 @@ def alch_worked_example_regression(rel, data, raw_text):
return errs
def _loss_reserve_stop(initial_capital, total_assets, reserve):
"""Loss-reserve stop comparator in the pinned direction (owner guidance
2026-08-29, governor review 10361, PR #8): stop fires only when the
loss EXCEEDS the reserve, i.e. when (initial capital - total assets)
> reserve. A loss makes the LHS positive; a profit (total assets >
initial capital) makes it negative and can never fire this stop. The
reversed 'total assets - initial capital' form is invalid because it
is negative exactly when a loss exists."""
return (initial_capital - total_assets) > reserve
def alch_accounting_regression(rel, data, raw_text):
"""Loss-reserve + fill-price accounting regression (owner guidance
2026-08-29 for governor review 10361, PR #8): the low-alch-members
entry must express the loss-reserve stop in the correct direction
EVERYWHERE ('initial capital - total assets > 162,990'), the reversed
sign form must never reappear, and the realized GP/hour bounds must be
derived from the declared fill-price scenarios (A live-low -3.00/cast
vs B max allowed +5% fill -30.15/cast), exposing the +5% fill loss
instead of recycling the live-low margin. Includes below/at/above
boundary fixtures and a profit-side negative regression for the
comparator itself. Scoped to the members low-alchemy entry only."""
if data.get("id") != "low-alch-members":
return []
errs = []
# 1. comparator boundary fixtures (pure function, entry's own constants)
INIT, RESERVE = 1_308_290, 162_990 # entry capital / 6-batch reserve
fixtures = [
("below (loss 162,989)", INIT - 162_989, False),
("at boundary (loss 162,990, strict >)", INIT - 162_990, False),
("above (loss 162,991)", INIT - 162_991, True),
("profit side (assets 50,000 above initial)", INIT + 50_000, False),
("breakeven (assets == initial)", INIT, False),
]
for label, total_assets, expect in fixtures:
got = _loss_reserve_stop(INIT, total_assets, RESERVE)
if got != expect:
errs.append(f"{rel}: loss-reserve comparator fixture '{label}' "
f"returned {got}, expected {expect}")
# 2. entry direction: correct form present, reversed form absent
if "initial capital - total assets > 162,990" not in raw_text:
errs.append(f"{rel}: loss-reserve stop must be expressed as "
f"'initial capital - total assets > 162,990' everywhere")
if "total assets - initial capital" in raw_text:
errs.append(f"{rel}: loss-reserve stop uses the reversed sign form — "
f"on a loss it is negative and can never exceed the "
f"reserve; use 'initial capital - total assets > 162,990'")
# 3. realized GP bounds must come from the fill-price scenarios
eco = data.get("economy", {})
if eco.get("gp_per_hour_low") != -29463:
errs.append(f"{rel}: gp_per_hour_low must be -29,463 (max-fill "
f"scenario B -30.15/cast x session-fast 977.2 casts/hr "
f"= -29,462.6), got {eco.get('gp_per_hour_low')}")
if eco.get("gp_per_hour_high") != -1758:
errs.append(f"{rel}: gp_per_hour_high must be -1,758 (live-low "
f"scenario A -3.00/cast x session-slow 585.9 casts/hr "
f"= -1,757.7), got {eco.get('gp_per_hour_high')}")
for pin, label in (("-30.15", "max-fill per-cast loss -30.15"),
("570.15", "max-fill per-cast cost 570.15"),
("423.15", "max-fill candidate price 423.15")):
if pin not in raw_text:
errs.append(f"{rel}: economy FILL-PRICE SCENARIOS missing {label} "
f"({pin}) — the +5% accepted-fill scenario must be "
f"declared so the realized GP bounds cannot silently "
f"revert to the live-low-only margin")
return errs
def main():
global STALE_DAYS
if "--stale-days" in sys.argv:
@ -134,6 +205,10 @@ def main():
# monotonic break/restock counters, scoped to the one-cast model
errors += alch_worked_example_regression(rel, data, path.read_text())
# loss-reserve direction + fill-price GP bound regression (owner
# guidance 2026-08-29 for governor 10361), scoped to low-alch-members
errors += alch_accounting_regression(rel, data, path.read_text())
# action id/step uniqueness
a_ids = [a["id"] for a in data["actions"]]
a_steps = [a["step"] for a in data["actions"]]