diff --git a/tools/validate.py b/tools/validate.py index 5947f6a..58f20ed 100644 --- a/tools/validate.py +++ b/tools/validate.py @@ -13,15 +13,18 @@ Runs: => WARNING (citation TODO — file a follow-up issue) - GE-margin entries (tags include flipping and/or merchanting — the unambiguous margin signals): the 2% GE convenience fee is - rounded DOWN to whole coins per item, so net_per_cycle MUST - equal income - floor(0.02*income) - cost. Fractional-tax - arithmetic (0.98*sell - buy) is an error. + rounded DOWN to whole coins per item and capped at 5,000,000 GP + per item, so net_per_cycle MUST equal + income - min(floor(0.02*income), 5_000_000) - cost. + Fractional-tax arithmetic (0.98*sell - buy) and uncapped tax + (6M on a 300M sale) are errors. NOTE: the generic `ge` tag alone does NOT gate this rule — a training/processing/procurement entry may legitimately carry `ge` while its income/cost model has nothing to do with flip tax. - Regression fixtures (--selftest): a fractional-tax flipping - entry must FAIL; a non-margin entry carrying only `ge` plus a - non-flip tag must PASS. + entry must FAIL; an above-cap high-value flip with the + correctly capped-tax net must PASS; a non-margin entry carrying + only `ge` plus a non-flip tag must PASS. 3. Global id uniqueness across the whole codex tree. Exit codes: 0 = clean (warnings ok), 1 = validation errors. @@ -69,7 +72,11 @@ def ge_floor_tax_error(rel, data): Wiki: "The 2% tax rounds down to the nearest whole number, so items sold for below 50 coins have a tax of less than one coin" — per-item - net MUST use the floored tax, never 0.98*sell - buy. + net MUST use the floored tax, never 0.98*sell - buy. The fee is also + capped at 5,000,000 GP per item (per-item cap, documented in the + flip-candidates best_practice/risk), so tax = min(floor(0.02*sell), + 5_000_000). Owner decision 2026-08-28: one global formula for all + entries (option A), no per-entry tax-model discriminator. """ tags = {str(t).lower() for t in data.get("tags", [])} if not tags & MARGIN_TAGS: @@ -78,13 +85,15 @@ def ge_floor_tax_error(rel, data): if not all(k in eco for k in ("income_per_cycle", "cost_per_cycle", "net_per_cycle")): return None sell, buy = eco["income_per_cycle"], eco["cost_per_cycle"] - tax = math.floor(sell * 0.02) + tax = min(math.floor(sell * 0.02), 5_000_000) expected = sell - tax - buy if eco["net_per_cycle"] != expected: return ( - f"{rel}: net_per_cycle {eco['net_per_cycle']} != floored-tax model " - f"{expected} (sell {sell} - floor({sell}*0.02)={tax} - buy {buy}); " - f"GE tax rounds down per item — fractional 0.98*sell - buy is invalid") + f"{rel}: net_per_cycle {eco['net_per_cycle']} != capped floored-tax " + f"model {expected} (sell {sell} - min(floor({sell}*0.02), 5_000_000)" + f"={tax} - buy {buy}); " + f"GE tax rounds down per item and is capped at 5,000,000 GP per item " + f"— fractional 0.98*sell - buy and uncapped tax are invalid") return None @@ -101,6 +110,13 @@ def run_selftest(verbose=False): {"income_per_cycle": 5000, "cost_per_cycle": 3000, "net_per_cycle": 2000}, False), ("correctly-floored flipping entry", ["flipping", "merchanting"], {"income_per_cycle": 244, "cost_per_cycle": 223, "net_per_cycle": 17}, False), + # sell 300,000,000 -> uncapped floor tax is 6,000,000; the per-item + # cap makes it 5,000,000, so the correct net is 1,000,000. The old + # uncapped rule demanded net=0 and rejected this valid entry. + ("high-value flip above the 5,000,000 GP per-item tax cap", + ["flipping"], + {"income_per_cycle": 300_000_000, "cost_per_cycle": 294_000_000, + "net_per_cycle": 1_000_000}, False), ] errors = [] for name, tags, eco, expect_error in fixtures: