Commit Graph

29 Commits

Author SHA1 Message Date
Claude
0c2e38c998 Merge origin/main 5ca8324b into issue-3; reconcile validator gates (governor 10637 / owner guidance 2026-08-29)
Magic/Alchemy PR #10 landed on main; this integrates it into the woodcutting
branch and resolves the predicted tools/validate.py conflict:

- union of all validator gates: magic/alchemy (recipe/rate/tier/loadout,
  supply_limit_conflicts, document_error + _end_to_end_bad_mapping),
  mining (pickaxe suite), GE-flip lifecycle (floor-tax + scheduler +
  conservation + slot-phase + buy-capacity + slot-reuse), and the
  woodcutting gates (canonical axe table, per-mode ladders, per-selected-
  row 27/28 dynamic rates, drop-GP-zero, prepare/failed/drop-gate wiring).
- governor 10637: removed the run_flip_selftest alias duplication — the
  combined GE/Woodcutting suite no longer runs twice. Each suite now runs
  exactly once per invocation: run_selftest (cross-field/alchemy),
  run_flip_selftest (GE lifecycle), run_mining_selftest, and the new
  run_woodcutting_selftest. Truthful unique selftest count: 144 PASS.
- tree scan: 6 entries, 0 errors, 0 warnings; git diff --check clean.
2026-08-29 15:44:34 -05:00
Claude
f569ce0f2c codex: reconcile issue-5 with current main (mining PR #11) — merge origin/main ccdd0c6, union schema/validator gates (bounce-fix attempt 8, governor mergeable=false finding, owner guidance 2026-08-29T21:45Z) 2026-08-29 15:00:10 -05:00
Claude
1c0322e0ef Merge remote-tracking branch 'origin/main' into issue-3
# Conflicts:
#	tools/validate.py
2026-08-29 14:51:01 -05:00
Claude
dfa7e00bd0 Merge origin/main into issue-3; bounce-fix 4: executable axe preparation, per-selected-row dynamic rates, drop-GP-zero (governor 10398, owner decision 10499)
- woodcutting-f2p.yaml v5: prepare state (a2-a9 check/withdraw/GE-buy/wait/collect/equip-or-carry/loadout -> travel), unobtainable axe -> failed (never complete), chopping->drop_logs gated to afk-xp, per-row chop/walk/respawn/log_price constants, mode rates = canonical top-row reference, woodcutting_fixtures (level-1 / level-50 quest+no-quest / level-70 / level-30 profiles), drop-mode realized GP 0
- schemas/entry.schema.json: ladder rows require per-row rate fields; model slimmed to drop/deposit/break; woodcutting_fixtures block added
- tools/validate.py: port woodcutting gates onto the economy validator (select_ladder_row + row_rates recompute fixtures at 27/28 capacity, drop-GP-zero gate, prepare/failed/drop-gating wiring checks); selftest now 55 checks
2026-08-29 04:53:03 -05:00
Claude
832e8e5048 Merge remote-tracking branch 'origin/main' into issue-5
Resolve schema/validator conflict by preserving both sides:
- main: GE flip entry validator (slot_state_machine, quote_age_policy,
  max-fill fields) + new economy entries (flip-candidates, low-alch-members,
  _f2p-alch-catalog)
- issue-5: rate_model lifecycle derivation, dynamic recipe selection,
  loadout verification, supply-fact consistency gate
2026-08-29 04:14:21 -05:00
Claude
7ee36da445 Merge remote-tracking branch 'origin/main' into issue-4
# Conflicts:
#	schemas/entry.schema.json
#	tools/validate.py
2026-08-29 03:30:20 -05:00
Claude
912b1ae432 codex: executable tier mutual-exclusivity — SII only 43 <= Magic < 55, HLA only >= 55 with target above 55, completion first at target (bounce-fix attempt 7, owner guidance 10529, governor 10477, issue #5) 2026-08-29 03:22:27 -05:00
Claude
2a5cfb30af Merge remote-tracking branch 'origin/main' into issue-4
# Conflicts:
#	tools/validate.py
2026-08-29 03:18:04 -05:00
Claude
d19a4d9de0 Merge remote-tracking branch 'origin/main' into issue-1
# Conflicts:
#	tools/validate.py
2026-08-29 03:13:45 -05:00
Claude
a0b03b87c6 codex: mining-f2p — level-1 pickaxe tie-break: Mining 1 selects+equips iron (bounce-fix attempt 6)
Owner guidance 2026-08-29 for governor review 10476 (PR #11), finding by finding:

1. Explicit tier rank/tie-break for equal Mining gates. pickaxe_tiers
   declared bronze and iron both at Mining 1 / Attack 1 while the
   level-1 worked example called bronze canonical and carried it (27
   slots) — internally contradictory under the entry's own
   highest-unlocked acquisition policy. pickaxe_tiers now carries an
   explicit rank (bronze 1 .. rune 6) and the selection rule resolves
   equal Mining gates by rank: Mining 1 deterministically selects IRON
   (7-tick roll, rank 2) over bronze (rank 1), and Attack 1 EQUIPS it
   (iron attack gate = 1) -> 28 ore slots. Stated at every policy
   layer (summary, accounts.requires_other, best_practice.equipment,
   prepare state, economy.supplies, a14 equip-tool gate, pickaxe_tiers
   head comment). An owned-only bronze run is documented as an operator
   choice, never the entry's selection rule.

2. Level-1 lifecycle/rate inputs recomputed. The level-1 worked
   example now uses the equipped iron pickaxe: 28 ore slots per drop
   load, iron cadence [4200, 6000] ms (midpoint 5.1 s), and the
   copper/tin rate re-derived from that cadence: 1.6 rolls x 5.1 + 0.5
   drop + 0.39 break = 9.05 s/ore -> 398 ores/hr -> ~6,960 XP/hr (pin
   6,900), replacing the bronze-midpoint 360 ores/hr / 6,300 XP/hr.
   Updated in mine_sites (se-varrock-copper / se-varrock-tin),
   economy comment block, profit_drivers, worktree example, and risk
   notes; no other rate changes (fast-xp iron 28,000 and banked rates
   untouched).

3. Recomputed 1-Attack/1-Mining fixture + capacity proof. Schema:
   pickaxe_tiers gains required rank (1..6); pickaxe_examples gain
   required expected_slots (28 equipped / 27 carried). Validator:
   select_pickaxe() ties by rank; every pickaxe_examples row is
   recomputed including expected_slots, the 1-Attack/41-Mining
   rune-carry fixture is preserved, and the 1-Attack/1-Mining row is
   now mandatory (selects iron, equipped, 28). Committed negative
   fixtures f18-f22: bronze-at-Mining-1, carried-at-Attack-1, wrong
   slots, missing 1/1 fixture, and a tier missing rank are all
   rejected (22 selftests pass).

Preserved: complete GE tool acquisition loop, structured copper/tin
below-15 fallback, fast-xp/banked-income modes, dynamic 27/28
inventory handling, declared banked targets, respawn/world-hop model,
and all prior rate gates. version 6 -> 7, updated 2026-08-29.

Gate: python3 tools/validate.py -> 2 entries, 0 errors, 1 pre-existing
F2P WARN (low-alch-f2p researched, untouched); --selftest -> 22 PASS;
git diff --check clean.
2026-08-29 03:12:40 -05:00
Claude
671d025d9f fix(flip-candidates): release the physical GE slot after a completed flip via closed -> free (governor 10471)
A successful sell followed sell-filled-awaiting-collect -> closed, and
'closed' had no outgoing transition while the scheduler left closed
slots untouched — every GE slot could perform ONE flip and then became
permanently unusable (no rotations, breaks, or longer sessions).

Fix (finding-by-finding, governor comment 10471):
1. 'closed' is no longer a sink. The completed cycle's ledger is
   reconciled and ARCHIVED at close, and the physical slot is released
   to free via the explicit archival transition closed -> free
   (flip-candidates.yaml slot_transitions, whens, scheduler_iteration,
   a9 archive step, summary, risk, sources). The scheduler pass
   dispatches the release (SLOT_DISPATCH['closed'] = 'release'), so a
   closed slot is only ever transient.
2. A deliberately closed cancelled sell releases identically:
   sell-cancelled-awaiting-collect -> closed -> free (whens updated).
3. Committed two-rotation regression (validator, wired into
   ge_lifecycle_errors for every flipping entry): slot_reuse_violations
   walks one physical slot through free -> buy-open ->
   buy-filled-awaiting-collect -> ready-to-sell -> sell-open ->
   sell-filled-awaiting-collect -> closed -> free -> buy-open (second
   buy in the SAME slot), validating every ledger frame against both
   conservation gates; archived_ledger_preserved_after_second_buy
   proves the rotation-1 archived ledger survives rotation 2 bit-for-bit
   (no loss, duplication, or mutation). Negative fixtures prove a
   closed sink or a closed that does not release to free FAILS.
4. ge_lifecycle_errors now requires closed == ['free'] exactly and
   rejects any machine where a state has no transitions.
5. Worked example: t5/t8 walk the release, t8b runs rotation 2 in the
   same physical slots.

All prior fixtures preserved (tax x4, async-slot scheduler,
collection-box, limit-window, side-aware conservation x25) — now 35
selftest checks.

Evidence: python3 tools/validate.py -> 2 entries, 0 errors, 1
pre-existing warning; --selftest -> 35 PASS, 0 errors; git diff
--check clean.
2026-08-29 03:11:20 -05:00
Claude
052c6984ac 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).
2026-08-29 03:00:56 -05:00
Claude
64b5e4890e codex: mining-f2p — executable GE purchase loop, structured copper/tin below-15 fallback, a2 target restrict (bounce-fix 5)
Governor bounce 5 (2026-08-28) repaired finding by finding:
1. Tool purchase is now executable end to end: new a10 ge_open before any
   ge_buy; a12 wait-ge-fill is BOUNDED (one 3-6 min fill window) and on
   no-fill/partial-fill TIMEOUT cancels the resting offer -> a13
   collects/refunds -> fail closed via recover (3 loops -> failed); a resting
   offer is never re-placed indefinitely. Actions renumbered a11..a17.
2. min_level 1 copper/tin below-15 path now has STRUCTURED fallback rows
   (mine_sites se-varrock-copper / se-varrock-tin, level 1, 17.5 xp, safe
   East Varrock) added to mining_modes.fast-xp.sites; level-1 and level-14
   selections are pinned in a new mining_selections block and recomputed by a
   new select_fast_site() validator gate (+ 3 committed selftest fixtures).
3. a2 no longer advertises silver/coal/gold/mithril banked targets: it
   restricts banked-income to the declared rows (iron @ se-varrock-bank,
   adamantite @ al-kharid-south-adamantite); superheat-coal technique is
   marked informational-only. New ge_open/ge_offers_done/ge_collect
   validator gate (+ 1 fixture). Modeled below-15 rate 6,300 XP/hr (was
   the unbacked '~8,800' prose) recomputed from the bronze cadence midpoint.

Validator: 2 entries, 0 errors, 1 pre-existing WARN; --selftest 17 PASS;
git diff --check clean.
2026-08-28 15:51:05 -05:00
Claude
8d85c65801 fix(low-alch/handoff)... 2026-08-28 15:15:23 -05:00
Claude
76f8e5ad27 fix(flip-candidates): GE asset accounting + offer lifetime per governor 10390 (bounce-fix 6)
- Buy-limit reset NEVER cancels an offer: quote_age_policy now aborts
  only on a stale scanned edge or explicit strategy reallocation; the
  4h window is a capacity clock (limit_ledger), not an offer deadline.
  Removed every lapse-as-cancel trigger from best_practice, whens,
  quote_age_policy, a5/a8, worked example, risk, and the a6 verdict.
- Completed offers pass through the collection box: new canonical
  sub-states buy-filled-awaiting-collect / sell-filled-awaiting-collect;
  a full buy cannot sell and a full sell cannot close before ge_collect
  (scheduler dispatches collect for all four *-awaiting-collect states).
- Works the arrowtips example side-aware: a 4,000/10,000 partial BUY
  cancel returns 4,000 units + refunded CASH for the unfilled 6,000 —
  never 'refunded units'; the phantom 10,000-unit withdrawal is gone.
- conservation_violations() is now side-aware (buy: quoted =
  filled + still_open + cancelled_unfilled with acquired items = filled
  only, cash = spent + still_committed + refunded; sell: quoted =
  sold + still_open + returned_unsold) with slot_phase_violations()
  phase gates and committed negative fixtures: phantom refunded buy
  units, a full buy sold before collection, a full sell closed before
  proceeds collection. Existing tax/scheduler/limit fixtures preserved.
- grammar (ge_cancel/ge_collect), schema sub_states minItems, entry
  version 5 + sources updated accordingly.
2026-08-28 14:48:55 -05:00
Claude
889c3076b6 validate: keep skipping underscore-prefixed dataset files (regression fix) 2026-08-28 04:22:01 -05:00
Claude
86c0c896d8 woodcutting-f2p: three explicit modes + corrected axe model (governor 10323 bounce-fix 3, owner decision 10288)
- fast-xp / afk-xp / banked-income modes with deterministic first-match
  ladders (no 'or'), level-1 fallbacks, pinned activity state
- axe model corrected: 41 Woodcutting to USE / 40 Attack to EQUIP;
  equip only when Attack permits, else carry the same axe — committed
  axe_examples incl. the 1-Attack/41-WC rune-axe carry case (owner)
- full per-mode lifecycle: prereqs, axe acquisition, initial inventory,
  travel, depletion/respawn, one chop attempt, inventory-full, complete
  dropping or banking route, breaks independent of incidental banking,
  recovery branches, completion
- per-mode worked examples: prep -> first full-inventory boundary ->
  first scheduled break -> resume (level-50 and level-70 pinned)
- realized rates: logs/hr = model chain (chop x capacity / cycle incl.
  walk/deposit or drop, x respawn duty x break duty); xp/gp derived
  and validator-enforced (axe_tiers / axe_examples / woodcutting_modes
  structured schema blocks + 16 committed selftest fixtures)
- wiki-verified axe table 2026-08-28; prices refreshed 2026-08-28
2026-08-28 04:21:31 -05:00
Claude
589767b195 codex: rate_model lifecycle derivation + executable HLA tier selection (bounce-fix attempt 5, owner guidance 10331, issue #5) 2026-08-28 04:04:03 -05:00
Claude
02b366c7ef flip-candidates: per-slot async sub-state lifecycle (governor 10323 bounce-fix 5)
- slot_state_machine: canonical 8 sub-states (free/buy-open/
  buy-cancelled-awaiting-collect/ready-to-sell/sell-open/
  sell-cancelled-awaiting-collect/ready-to-requote/closed) + global
  scheduler pass that never advances unrelated slots
- ge_cancel moves filled items/refunds/unsold units/coins to the GE
  collection box; ge_collect + ledger reconciliation before reuse
- 4h buy-limit ledger (limit_used/first_fill_ts/limit_resets_at) distinct
  from offer expiry; cancellation never renews the limit
- per-slot conservation invariants w/ committed validator fixtures
- economy.ceiling_gp_per_hour typed ceiling, gp_per_hour_low/high as
  documented realized scenario bounds (no uncited multipliers)
- capped/floored 2% tax + 4 fixtures preserved (owner decision)
- action-grammar: ge_cancel/ge_collect rows corrected
2026-08-28 04:01:20 -05:00
Claude
a197b34aa8 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.
2026-08-28 03:36:45 -05:00
Claude
765505ed0f codex: structured mining data model + respawn/success-correct rates + executable tool prep (bounce-fix 4, owner mode decision)
Governor bounce 4 + owner guidance 2026-08-28 (structured, provable, full lifecycle):

1. site model is data, not prose: new mine_sites table (ore, gates, rock_count,
   respawn_seconds, rolls_per_ore, modeled_ores_per_hour, world_hop, selectable,
   inventory_policy, access, safety, route). Adamantite @ Al Kharid south is now
   respawn-BOUND (2 rocks, 240 s respawn): modeled 120 ores/hr ONLY with world
   hopping (wiki F2P adamantite guide) = 70,080 gp/hr; no-hop ceiling ~30/hr =
   17,520; the 839 ores/hr iron-cadence fantasy is gone and a cadence-ceiling
   gate rejects it. Runite is selectable:false (Wilderness PK risk, ~12 min
   respawn) — refused, not hand-waved. New hop state + hop_world action (a14)
   implement the world-hop policy; long-respawn selectable sites REQUIRE
   world_hop + a hop_world action (validator).
2. fast-xp success model: rolls_per_ore 1.33 (61+, derived from the wiki 52,500
   benchmark: 1,500 ores/hr / 2,000 3-tick rolls/hr); rates recomputed with
   success rolls + respawn math (3 rocks x 3.59 s = 10.8 s > 5.4 s respawn ->
   zero idle, pinned) + drop/bank + breaks: xp_per_hour 28,000 (was 32,400 with
   a flat 0.3 s and 1 ore/click); 52,500 stays a labeled external benchmark.
3. sub-29 fast-xp preserved: South-east Varrock DROP site (safe, same mode) —
   the mode never silently switches to banking.
4. tool prep executable end to end: check-tool (has_item) -> withdraw-tool
   (owned branch, bank_inventory) or buy-tool + wait-ge-fill (ge_offers_done) +
   collect-tool (ge_collect, verify) -> equip-tool via menu_option Wield with
   the Attack gate; recovery on purchase/equip failure (3 -> failed).
5. VALIDATOR PROVES THE CONTRACT (no phrase-search): pickaxe_tiers must match
   the canonical F2P table exactly (Mining=use gate, Attack=equip gate,
   roll_ticks); pickaxe_examples recomputed per row (1-Attack/41-Mining must
   select + CARRY rune; 40-Attack/70-Mining equips; 5-Attack/30-Mining carries
   mithril); both modes declared with fixed inventory policies; mode refs must
   resolve to declared selectable policy-matching sites; Wilderness selectable
   -> error; modeled ores/hr <= 3600/(rolls_per_ore x 2.7 s); xp_per_hour
   within 2% of the modeled fast-xp rate. 13 committed --selftest fixtures
   (negative cases included) wired into every gate run.

Evidence: validate 2 entries 0 errors 1 pre-existing WARN; --selftest 13 PASS;
synthetic 3-way merge main 79cab78 (incl. catalog) + head clean + gate green;
git diff --check clean. version 4 -> 5.
2026-08-28 03:34:48 -05:00
Claude
126cf7b38a codex: mining-f2p — fast-xp/banked-income modes, Mining-vs-Attack pickaxe gates, modeled rates (bounce-fix attempt 3)
Owner decision 2026-08-28: expose separate fast-xp (powermine, drop) and
banked-income (deterministic target ore + mine, bank) modes; never select
highest-available rocks. Separate pickaxe gates per tier: Mining level
decides usability (rune 41), Attack only equipping (rune 40) — pinned
1-Attack/41-Mining carry-and-use example. Modeled policy rates recomputed
with cadence, respawn idle, drop/bank legs and breaks (fast-xp ~32,400
XP/hr; banked iron ~56,500 gp/hr); 52,500 kept as labeled external
benchmark only. Worked examples per mode covering preparation -> first
full inventory -> first break -> resume. Validator now proves the gate
distinction and both modes for mining entries.
2026-08-28 03:15:04 -05:00
Claude
34f36d2275 codex: dynamic SII recipe selection + realized rates + validator crash fix (bounce fix attempt 3, owner guidance)
Owner decision (2026-08-28): Superheat Item uses a dynamic recipe table
governed by Smithing level — highest UNLOCKED recipe selected, primary ore
(target_input, never coal) is the spell target, fallback to the next lower
unlocked recipe, else skip SII and stay on LA.

- magic-alch-handoff: per-recipe target_input; selection block
  (mode highest-unlocked / eligible_rule / fallback); full-lifecycle
  actions (buy-staff, equip-staff w/ fire-rune failure path, buy-supplies,
  cast-alch, cast-superheat, bank-loop, take-break); deterministic restock
  (LA/HLA carried_batch 1,000 + threshold 100 -> 901-cast batches; SII
  24-cast bank cycles, bars >= 24); breaks [400,700] COMPLETED casts,
  independent of banking; worked example pinned (prep -> first restock @24
  -> break @550 -> resume -> restock @552); stop conditions + recovery block
- rates REALIZED incl. mandatory breaks + banking: LA 24,500 / SII 27,500 /
  HLA 51,400 xp/hr (formulas pinned; loop-only 837/hr and 305/hr regimes
  superseded); low-alch-f2p also switched (xp 24,500, gp -110,500..-74,000)
- validate.py: rows now collected ONLY after schema validation (schema-
  invalid MAPPINGS incl. 'economy: not-a-mapping' produce normal ERRORs,
  never AttributeError tracebacks — end-to-end subprocess regression added);
  new gates: recipe_model_errors (target_input in declared inputs, coal
  never targetable, unique smithing gates, selection block + SII/fallback
  transitions) and rate_errors (xp_per_hour must match the realized cadence,
  tolerance 2%); highest_unlocked_recipe helper + 6 eligibility fixtures
- entry.schema.json: optional selection block + per-recipe target_input
- Evidence: validate 2 entries 0 errors 0 warnings; --selftest 31 PASS;
  governor's exact direct repro supply_limit_conflicts([('bad.yaml',
  {'economy':'not-a-mapping'})]) -> [] no crash; synthetic 3-way merge of
  main 79cab78 (incl. issue-6 catalog) + head: clean, gate green; git
  diff --check clean
2026-08-28 03:11:07 -05:00
Claude
00bfc358cb fix(validate): cap GE floor tax at 5,000,000 GP per item (owner decision A)
Owner decision 2026-08-28: single global GE tax formula for all entries:
min(floor(0.02 * sell_price), 5_000_000). Apply the per-item cap inside
ge_floor_tax_error(); the margin-tag scope (flipping/merchanting) and the
generic-ge non-margin exemption are unchanged.

Regression fixtures now cover:
- fractional-tax flipping entry must FAIL (kept)
- non-margin entry with only ge + processing must PASS (kept)
- correctly-floored flipping entry must PASS (kept)
- high-value flip above the 5,000,000 GP cap (sell 300,000,000 / buy
  294,000,000 / net 1,000,000) must PASS — the old uncapped rule computed
  6,000,000 tax, demanded net 0, and rejected this valid entry (new)

Closes the governor's attempt-3 blocker on PR !7.
2026-08-28 02:35:53 -05:00
Claude
75a9965c9f codex: bounce-fix attempt 2 — additive pacing, cast-unit breaks, concrete SII recipes, validator crash guard (issue #5)
Governor-review (PR #10, attempt 2) findings, same issue-5 branch:

1. Pacing is additive. action-grammar.md defines anti_ban.delay as sleep
   AFTER the action, so a cast takes 1.8s + 1.6-3.4s = 3.4-5.2s (mid
   4.3s), ~837 casts/hr, not 1,400 or 1,800. Both entries now pin the
   midpoint rate with the formula documented: LA 25,953 / SII 44,372 /
   HLA 54,419 xp/hr. low-alch-f2p gp_per_hour bracketed by the delay
   endpoints (-119,000 / -78,000).

2. run_control units agree: schema.md 'every' counts iterations and
   iteration is one cast, so break.every [8, 14] is casts (comment no
   longer claims bank cycles).

3. SII is now a concrete recipe. New top-level recipes: table models all
   eight F2P smithable bars (bronze/iron/silver/steel/gold/mithril/
   adamantite/runite) with every input and quantity (coal 2/4/6/8,
   Smithing gates, bar ids) from the OSRS wiki smelting table. The
   cast-superheat action pins params.recipe (steel-bar anchor) plus a
   recipe_rule: smithing gate + complete input cost + one nature rune <
   bar price; buy/withdraw the exact unnoted inputs; bank the bar.
   entry.schema.json gains the optional 'recipes' section. Validator
   gains recipe/SII checks and a deterministic steel-bar fixture
   (1 iron + 2 coal = 3 inputs; margin 600 - (100 + 2*150 + 145) = 55).

4. Validator no longer crashes on malformed docs: scalar/empty YAML is
   rejected with a normal ERROR (document shape check) and
   supply_limit_conflicts guards non-mapping rows; regression fixtures
   cover (bad.yaml, None), scalar docs, and undeclared recipe ids.

Tests: python3 tools/validate.py -> 2 entries, 0 errors, 0 warnings
(exit 0); --selftest -> 14 PASS; live negatives: scalar + empty docs
error cleanly (exit 1, no traceback), reintroduced 12500 divergence is
caught (supply-fact gate), git diff --check clean.
2026-08-27 17:01:54 -05:00
Claude
9a2f936c05 fix(codex): pin alch cadence to delay policy, unify nature-rune limit, phase-aware SII (issue #5)
Governor-review bounce (PR #10) fixes on issue-5:

1. Timing units + rates: one OSRS tick = 0.6s so a 3-tick cast is 1.8s,
   not 5.4s as both action comments claimed. The mandatory 1.6-3.4s
   post-action delay (mid ~2.5s) allows ~1,440 casts/hr before overhead;
   rates are now pinned at 1,400 casts/hr: LA 43,400 / SII 74,200 / HLA
   91,000 xp/hr (was 55,800 / 95,400 / 117,000 @ 1,800). The old 1,800
   figures were unreachable under the entries' own pacing.

2. Nature rune limit unified to 18000 (mapping 'limit' + Module:Exchange
   agree, fetched live 2026-08-27): low-alch-f2p 12500 -> 18000, matching
   magic-alch-handoff. tools/validate.py gains a cross-entry supply-fact
   consistency check (same item+source must agree on ge_limit; run on
   every gate invocation) plus --selftest regression fixtures: diverging
   limits must fail, agreeing limits must pass, missing ge_limit is not
   gated.

3. SII tier is now phase-aware and translatable: actions split into
   cast-alch (LA/HLA -> cheapest alch candidate, noted stack) and
   cast-superheat (SII 43-55 -> unnoted ore whose live bar margin is
   positive; never a noted stack, never a price-only pick; on_fail skip
   when no positive-margin ore). State machine pins each tier path
   (LA <43, SII window chosen/skipped 43-55, HLA >=55), bank_supplies
   banks smelted bars, buy-supplies restricts ores to positive-margin
   recipes.

Tests: python3 tools/validate.py -> 2 entries, 0 errors, 0 warnings;
--selftest -> 3 PASS; negative test (reintroducing 12500) caught by the
new check; git diff --check clean.
2026-08-27 12:46:12 -05:00
Claude
f6675ba543 fix(validate): scope GE floor-tax gate to margin tags, add regression selftest
Governor-review bounce (PR #7, attempt 2) fix on issue-1:
- The floor-tax rule previously triggered on any entry carrying the
  generic 'ge' tag (tags & {flipping, merchanting, ge}). A training,
  processing, or procurement entry may legitimately carry 'ge' while
  its income/cost model is unrelated to flip tax, so it could fail the
  global gate unless its economics accidentally satisfied the flip
  equation.
- Rule now gates ONLY the unambiguous margin tags (flipping and/or
  merchanting); the bare 'ge' tag no longer triggers it.
- Rule extracted into ge_floor_tax_error() and a --selftest mode added
  with three regression fixtures, run on every gate invocation:
    * fractional-tax flipping entry  -> must FAIL
    * non-margin entry with only 'ge' + 'processing' (economics
      deliberately violate the flip equation) -> must PASS
    * correctly-floored flipping entry -> must PASS
- Demo (old scope): ge+processing fixture with net 2000 vs floored 1900
  errored under the old tag set; low-alch-f2p's -112 vs -115 model
  would also false-positive if it carried 'ge'.

Tests: python3 tools/validate.py -> 2 entries, 0 errors, 1 pre-existing
warning (researched flag on low-alch-f2p); --selftest -> 3 PASS, 0 errors;
git diff --check clean.
2026-08-27 12:34:08 -05:00
Claude
864f2c9e1c fix(flip-candidates): floor GE tax to whole coins, pin full sell-cycle, qualify F2P path
Governor-review bounce (PR #7) fixes on issue-1:
1. GE 2% convenience fee rounds DOWN per item: net = sell - floor(0.02*sell) - buy.
   Anchor net 16.12 -> 17 (244-4-223); top-8 members table recomputed with
   floored tax -> 598,000/4h = 149,500 gp/h (was 138,000); gp_per_hour_low/high
   updated. Validator now rejects fractional-tax arithmetic for GE-margin entries.
2. State machine split wait_fill into wait_buy_fill / wait_sell_fill so the full
   buy -> buy-fill -> sell -> sell-fill -> collect path is explicit; sell cycle
   can no longer loop back into sell without collecting.
3. Economy qualified as the members path (8 slots); F2P path added (3 slots,
   F2P-tradeable rows only): Iron bar + Cosmic rune + Silver ore = 227,000/4h
   = 56,750 gp/h. Members/F2P flags cross-checked against wiki item pages.
2026-08-27 05:27:09 -05:00
Vale
986fc1a6a8 Codex skeleton: schema contract, action grammar, data-sources rules, JSON schema, validator, golden example (low-alch-f2p, wiki+price verified), README 2026-08-27 02:59:55 -05:00