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.
- 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.
Owner lifecycle addendum (2026-08-28) + governor bounce 4 (lifecycle contract):
- state machine now covers the ENTIRE buy -> sell -> collect flow with no
stranded state: partial fills tracked per slot (filled_qty ledger), 4h-lapse
buys go to a new cancel_requote state (ge_cancel: units return to inventory,
cash refunds, slot freed) instead of collect, cancelled units still sell at
the high edge, lapse sells re-quote or lower rather than strand
- action grammar + schema extended ONCE with the cancellation mutation
ge_cancel (was missing; ge_clear/ge_offers_done are predicates only) and
used explicitly in the new a8 cancel-requote action
- concrete runtime state: minimum per-slot capital table (buy x 4h limit),
members 8-slot vs F2P 3-slot paths, per-slot offer phase + quantities,
buy-limit window, inventory/collection reconciliation in a9
- breaks connected to an explicit safe/resume path: resting offers persist
server-side + per-slot ledger survive the pause; resume = re-open GE,
re-check offers, continue current phase; never break mid-collect/mid-place
- rates qualified against the full lifecycle: 149,500 gp/h is named the 4h
buy-limit CEILING; realized range = ceiling x fill_factor (~0.5-0.9) x
duty_cycle (~0.90-0.95) ~ 96,300 gp/h midpoint; gp_per_hour_low 74,750
- pinned worked example: capital 22.5M -> 8 slot quotes -> arrowtips partial
fill (4,000/10,000 tracked) -> iron bar full fill sell (90,000 net) ->
cosmic no-fill ge_cancel + requote -> arrowtips partial matures at lapse
(cancel, sell 4,000, 68,000 net) -> first break after 6-10 rotations ->
resume -> stop conditions
- version 2 -> 3; evidence: validate 2 entries 0 errors 1 pre-existing WARN,
selftest 4 PASS (fractional-tax fail, ge non-margin pass, floored pass,
above-cap pass), synthetic merge main 79cab78 + head clean + gate green,
git diff --check clean
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.
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.
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.
Implements the epic's uncovered P0 economy backlog item (flip candidates).
Verified source chain 2026-08-27: prices.runescape.wiki mapping + latest,
all 15 shortlist limits cross-checked against Module:Exchange (agree);
GE tax verified at 2% since 2025-05-29 (1% at 2021 intro), 5M/item cap,
sub-50gp effectively untaxed; Merchanting + Grand Exchange/Buying limits
pages cited for mechanics. Net model: 0.98*sell - buy at band edges.
Gate: python3 tools/validate.py exit 0 (2 entries, 0 errors, 1 pre-existing
warning on the golden entry's researched flag — none added by this entry).