Commit Graph

17 Commits

Author SHA1 Message Date
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
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
d19a4d9de0 Merge remote-tracking branch 'origin/main' into issue-1
# Conflicts:
#	tools/validate.py
2026-08-29 03:13:45 -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
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
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
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