o
    û1³jz  ã                   @   s,   d Z dZdZdZdjeed�Zd e¡ZdS )ao  The version stamp that says which pricing logic wrote a row's prices.

WHY THIS EXISTS
---------------
On 2026-09-16, answering "which rows still carry pre-fix prices?", every
timestamp and marker column in the deals table was checked and **not one of them
dates a row's pricing**:

* `last_seen_utc` and `source` are rewritten by ALL THREE paths - heavy
  (`smart_ingestor.py:580-581`), light (`:574-575`) and Stale Rescue
  (`:274-275`). `source` records who touched a row LAST, not who priced it, so a
  row priced by the heavy path on 09-10 reads `stale_rescue` today.
* `Deal_found` is Keepa's `creationDate` (`stable_deals.py:83`), not a write time.
* `last_update` is deliberately never populated (AGENTS.md 7.3).
* `Inferred_Sale_Count` looks like it should work and does not. ASIN 0415009804
  carries a count AND a pre-fix price: rows priced between 2026-09-11, when that
  column shipped, and 2026-09-12 16:50 UTC, when the price-association fix went
  live, have both. Confirmed against production.

So the version stamp is the only way to tell a current price from a stale one.

THE NULL RULE, AND WHY IT IS THE OPPOSITE OF THE Inferred_Sale_Count RULE
------------------------------------------------------------------------
**A row whose `Pricing_Logic_Version` is NULL, or lower than
`PRICING_LOGIC_VERSION`, carries STALE PRICING and is due a heavy re-fetch.**
NULL means "priced by unknown logic", which for scheduling purposes is the same
answer as "priced by old logic".

That is deliberately the reverse of the `Inferred_Sale_Count` rule in
`Documentation/INFERRED_PRICE_LOGIC.md` 4.C, which states that a NULL count means
"never computed" and **must never be read as zero and must never be used to hide a
deal**. The two rules do not conflict, because they answer different questions:

    Inferred_Sale_Count  ->  may this deal be SHOWN to a subscriber?
    Pricing_Logic_Version ->  does this row need WORK scheduled against it?

Being unsure about the first must never hide a deal, because hiding a deal on a
guess costs the subscriber a real opportunity. Being unsure about the second
costs about 7 Keepa tokens and re-prices a row that may not have needed it. The
asymmetry in the cost of being wrong is the whole reason the rules differ.

Do not "harmonise" them.

NO BACKFILL
-----------
Every row that exists today lacks this column, and none of them may be given a
value except by actually recomputing their prices. A backfill would have to guess
which logic wrote a row, and guessing is precisely what this column exists to
stop.

BUMPING IT
----------
Raise `PRICING_LOGIC_VERSION` by one whenever a change alters the prices the
pricing pipeline produces - `infer_sale_events`, `analyze_sales_performance`,
`get_1yr_avg_sale_price` or anything they call. `repair_pricing.py` selects on
`version IS NULL OR version < PRICING_LOGIC_VERSION`, so a bump is the entire
mechanism for scheduling the next repair sweep: no new script, no new predicate.

Do NOT bump it for a change that leaves prices identical (a refactor, a log line,
a docs edit). A spurious bump schedules a full re-fetch of every row in the
database.

A bump also has an IMMEDIATE, VISIBLE effect as of September 2026: Prime Picks
reads `CURRENT_PRICING_PREDICATE` below, so the moment a bump deploys, every row
in the table is stale and Agent's Choice empties out - on the next run, and for
read-time display straight away. It refills as the sweep repairs rows. That is
the intended behaviour (an empty Agent's Choice is better than a recommended
price the system knows is superseded), but it is not a subtle change, so expect
it rather than diagnosing it. The main dashboard grid is NOT affected.
é   zPricing Logic VersionÚPricing_Logic_Versionz(("{col}" IS NULL OR "{col}" < {version}))ÚcolÚversionzNOT {}N)Ú__doc__ÚPRICING_LOGIC_VERSIONÚPRICING_VERSION_HEADERÚPRICING_VERSION_COLUMNÚformatÚSTALE_PRICING_PREDICATEÚCURRENT_PRICING_PREDICATE© r   r   ú6/var/www/agentarbitrage/keepa_deals/pricing_version.pyÚ<module>   s    Vÿÿ