in reviewhumanPR #297Transport
[Fellowship Challenge] haggle.expiring.v1 - an offer lapses; a stale acceptance is refused
Submission for the Hexaware x Project NANDA Fellowship Technical Challenge
Author
@Ashish-Kumar-Dash
github profile →- Status
- In review
- Opened on
- Oct 1
- Branch
- challenge/Ashish-Kumar-Dash-expiring-offers
Description
The pitch.
Submission for the Hexaware x Project NANDA Fellowship Technical Challenge
**Lane:** Lab
**Gap.** The negotiation layer has no notion of time. `haggle.v1.accept()` honours the last offer whenever it is called, so a counterparty can sit on a favourable quote and cash it in arbitrarily late. Every TTL in the town lives elsewhere — memory, registry caching, payment timelock; bargaining has none, and `AgentMessage.expires_at` (records.py) is defined but unused in the Lab.
**Layer.** `negotiation` — new plugin `haggle.expiring.v1`: an offer is acceptable only for `OFFER_WINDOW_SECONDS` after it is made; a later acceptance emits `offer_expired` and returns no price. Ships with a bundled `expiring_offer` scenario, an `expiring_buyer` role that honours the acceptance result, and an `expiring_offer` validator.
**Fault.** The existing transport `delay` fault on a `nego_counter` pushes the acceptance **5.1s** past the **1.0s** window — no new fault code.
**Invariant (holds in the enforced run).**
- `offer_validity` — no offer is accepted past its validity window (layer-level, agent-independent, recoverable from the trace).
- `no_stale_settlement` — no escrow settles against a lapsed offer.
**Negative control.** Same run, rule off (`--swap negotiation=haggle.v1`) — **fails both stages, for the predicted reason**:
- `offer_validity` → *"accepted an offer 5.1s after it was made; the window is 1.0s"*
- `no_stale_settlement` → *"escrow settled against an offer accepted past its window"*
**Reproduce.**
nandatown run expiring_offer # enforced: both stages Passed
nandatown compare expiring_offer --swap negotiation=haggle.v1 # baseline PASSED, swapped FAILED
nandatown verify runs/ # hashes match; evaluator reproduces the result
python -m pytest tests/test_expiring_offer.py # red before the change, green after
**Verification.** `nandatown verify` passes on the enforced bundle ("hashes match and the evaluator reproduces the recorded result"). `tests/test_expiring_offer.py` fails before the change (`UnknownPlugin: no plugin 'haggle.expiring.v1'`) and passes after. Full suite: **1759 passed, 1 skipped**.
**Compatibility.** Additive only. `haggle.v1` and every existing scenario, role, and validator are untouched; the sole edit to existing code adds `expiring_offer` to the canonical scenario list in `tests/test_sim.py`. No shared records or schemas changed.
**What the evidence does not prove.** The layer invariant is airtight; the *system* effect (no payment) relies on a cooperating buyer. A hostile buyer could still send a unilateral `purchase_order` — gating that is a payments/coordination concern, not negotiation's.
**Next.** The window is a module constant because the Lab has no per-layer config channel (only transport/privacy get `.configure()`). A `layer_config` field on `ScenarioSpec` threaded to an optional `plugin.configure(cfg)` would make this and many plugins tunable from YAML.
**Bundle fingerprint:** sha256:595cd1b3e3e8b25397b595b2654e36ecc8f8122ca07103af1745280ed3fa6ffd
Try it
Open PR on GitHubView diffCheckout locally
git fetch origin pull/297/head:pr-297
git checkout pr-297