in reviewhumanPR #306Negotiation
[Fellowship Challenge] negotiation: posted-price clocks, Dutch and IOC-limit rising, that hold a delayed accept to a safe price
Submission for the Hexaware x Project NANDA Fellowship Technical Challenge
Author
@divij-pawar
github profile →- Status
- In review
- Opened on
- Oct 2
- Branch
- challenge/divij-pawar-dutch-auction
Description
The pitch.
Submission for the Hexaware x Project NANDA Fellowship Technical Challenge **Track:** Lab · **Layer:** negotiation · Adds two new ways to sell at a changing price, `dutch.v1` and `rising.v1`, plus four scenarios. Nothing that already exists is changed; the default `haggle.v1` still works as before. **Why both directions**: they are one feature. The same clock, roles, fault and checks are used for both, and only the rule for a late "yes" differs. Shipping only the falling version would leave an obvious way to cheat as soon as the price can rise. ## The gap Until now, the only way agents in the town could agree a price was haggling, taking turns making offers. There was no way to sell at a price that changes on its own over time, so nobody had tested this situation: **a buyer says "yes" to a price, but the message arrives late, after the price has already changed.** Which price should the buyer pay? The town's existing money check doesn't catch a wrong answer here. If the buyer is charged the wrong price, the money that left the buyer still equals the money the seller received, so the books balance and the check passes. ## What I added The seller posts a price that changes by a fixed step on every tick. The buyer accepts by naming the tick and price it saw. The right outcome for a late "yes" depends on which way the price was moving: - **Price falling (`dutch.v1`).** The sale happens at the price the buyer accepted, even if the message arrives late. This is how Dutch flower auctions work: pressing the button locks the price. It's safe because on a falling clock any earlier price is *higher* than today's, so a buyer can never gain by sending an old "yes". - **Price rising (`rising.v1`).** Now an earlier price is a *bargain*, so honouring it would let a buyer grab a price the seller no longer offers. Instead, the price the buyer named is treated as the **most they're willing to pay**. If today's price is still within that, the sale happens at today's price; if not, the sale is refused and nothing is charged. Stock exchanges handle late orders the same way, with a "limit order" that is filled immediately or cancelled. In both cases: **the buyer never pays more than they agreed to, and the seller never sells below its current price.** A made-up price, or one the seller never posted, is refused. If the same "yes" arrives twice, the item sells once. ## The fault The town's existing `delay` fault holds the buyer's "yes" for 1 second. That's four price changes while the message is in transit. No new fault code was needed. ## The proof: a deliberately wrong version of each rule Each rule has a "twin" that differs in exactly one function (`_sale_price`) and makes the obvious mistake: | Scenario | The wrong twin… | Result | |---|---|---| | `dutch_auction_naive` | charges whatever the price is when the "yes" arrives | **Fails:** sold at 1600 when the buyer agreed to 1800 (paid 3200 instead of 3600) | | `rising_clock_stale` | honours the old price | **Fails:** sold at 1500 while the seller was asking 1700 | In both failures, the town's money check still passes. That's the point: only the new checks catch the wrong price. `nandatown compare` runs each pair with the same agents, seed and delay. The only difference is the rule, and only the price and payment checks change. ## Evidence | Run | Result | Bundle fingerprint | |---|---|---| | `nandatown run dutch_auction` | PASSED | `sha256:76a67119fef11f6cef6f25d8d7481187245a1c0ba3bf5017acaa60a5d6caad6a` | | `nandatown run dutch_auction_naive` | FAILED, as intended | `sha256:85979db68a17ffebf5456c770722730efea4fda7a11855e71f9a3f8e1306cebd` | | `nandatown run rising_clock` | PASSED (late "yes" refused, nothing charged) | `sha256:233df2040670062630c49512c2e5e7953162c8a4314cd87f9f044a1f40ea9402` | | `nandatown run rising_clock_stale` | FAILED, as intended | `sha256:adb8e9a72eaa4658f1af5016ac61595ac106fb7952b6cb105fe8a4c3a1629132` | `nandatown ver …
Try it
Open PR on GitHubView diffCheckout locally
git fetch origin pull/306/head:pr-306
git checkout pr-306