test(seller): pin the award-dedup contract on two distinct award events (#279) - #282
Merged
Conversation
…ts (#279) The seller dedups awards on two different keys, and neither covers a retry arriving mid-flight. Two legs, outcomes declared before either was written so neither could be retrofitted to whatever the code happened to do. Leg 1 — post-terminal, expected GREEN. A fresh award id for a Delivered job is `Awarded::New` at the record layer, so the event-id key does NOT catch the retry shape; the job row survives the re-attempt; and the job-state guard is what refuses the second run. It asserts that composition rather than the predicate alone, because the record layer's answer is what decides whether the guard is load-bearing. It also asserts the contrast case — a re-seen id is `Duplicate` — so both shapes and both keys are distinguished in one place. Leg 2 — mid-flight, expected RED as a finding (#279). Same shape while the job is `awarded`/`executing`: the record layer says New, the guard says proceed, and nothing between them refuses, so a second execution is admitted. It is written as a PASSING tripwire that goes red when the per-job in-flight lock lands, rather than an `#[ignore]`d failing test — an ignored test reports nothing whether the hole is open or closed. Leg 1 is red-proven: making the award path clobber the job row's state back to `awarded` fails it on that exact assertion. Also corrects a neighbouring assertion message. It claimed a delivered job "must not re-execute on a duplicate award", but that test never records a second award — it checks the state predicate. It now says what it asserts, and leg 1 carries the duplicate-award claim it was standing in for. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This was referenced Jul 30, 2026
orveth
added a commit
that referenced
this pull request
Jul 30, 2026
…230, #287) Pre-existing test-only flake, not a #282 regression and not a production defect. The overspend race test re-awarded a hardcoded presumed winner (format!({:064x},0)); when scheduling left agent 0 among the two refused, that call was a fresh 30-sat reserve against 10 available and reserve() correctly refused it — read as an idempotency failure when the guard did the right thing. The aggregate asserts above the panic (winners.len()==3, reserved==90) were passing throughout. Fix: capture the actual race winners and re-award winners[0], making the idempotent-re-award leg deterministic. Test-only +17/-8, all in mod tests. Gated: money-path row green 641/0/2 by structural max-N+presence rule on the live head; seller-orch red-proved the panic byte-for-byte (requested:30/available:10/bound:Wallet) by pointing the re-award at a never-awarded id. Landed by seller-orch per buyer-orch(#230 owner)+lead ruling, buyer-orch reviews post-hoc.
This was referenced Jul 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Tests only — no production code changes. Pins the seller's award-dedup contract and files the hole in it as #279.
What the two legs are for
The seller dedups awards on two different keys, and the shapes they catch are not the same shape:
awards.award_idPK) ⇒ catches a re-seen award id (relay redelivery).Delivered/Paid/Failed⇒ skip) ⇒ catches a redundant award for a terminal job.A buyer retry produces neither: a fresh event id for a job we already hold. So covering redelivery says nothing about retries, and the two legs test that boundary from both sides. Both outcomes were declared before either test was written, so neither could be retrofitted.
Leg 1 —
a_fresh_award_id_for_a_delivered_job_is_recorded_but_never_re_executed(green)Two distinct award ids, one job id, job
Delivered. Asserts the composition, not the predicate alone:Awarded::Duplicate(the contrast case, so both keys are distinguished in one place)Awarded::New— the record layer does not catch a retryDelivered— the re-attempt does not clobber terminal stateStep 2 is the point. Asserting only the predicate leaves the record layer's answer unstated, and that answer is what decides whether the guard is load-bearing at all.
This is not already covered.
resume_selects_awarded_or_executing_not_delivered_or_terminaltests the pure predicate.delivered_or_paid_job_is_not_re_executedasserts "a delivered job must not re-execute on a duplicate award" — but it never records a second award; it drives one award toDeliveredand checks the predicate. That message named a property the test did not construct, so this PR corrects it to say what it actually asserts and leg 1 carries the duplicate-award claim.Red-proven. Changing the award path's
INSERT OR IGNORE INTO jobsto an upsert that writesstate = 'awarded'fails leg 1 on exactly the intended line:Leg 2 stayed green under that break, correctly — it does not depend on terminal state.
Leg 2 —
mid_flight_second_award_is_admitted_for_execution_no_per_job_in_flight_lock(#279)Same shape, but the job is
awarded/executing(both are exercised). The record layer saysNew, the guard says proceed, and nothing between them refuses — so a secondexecute_jobis admitted for a job already running.⚠ This test passing is the bad news. It is a tripwire: it asserts the unguarded behaviour and goes red the moment the per-job in-flight lock lands, at which point the fixer inverts one assertion and closes #279. The doc comment says so in place.
Why a tripwire and not
#[ignore]: an ignored test reports nothing whether the hole is open or closed — the failure mode is silence, which is the class of defect this lane has spent the day removing. The hazard is real and worth naming: CI now enforces a known-wrong behaviour, so anyone who fixes the hole sees a red test. That is intentional and the comment routes them, but reviewers should agree it is the right trade before merging.What #279 costs — and does not
Not double payment; that is blocked further down (reservation PK, single job-keyed watcher, outbox dedup) — verified by the mobee lead, not by this PR. The costs are a duplicate agent process, a slot-accounting bypass (the first award already removed the parked permit, so the second execution holds no permit and the node exceeds its configured concurrency invisibly), and a second push that can move the branch tip off the journaled commit — which the buyer refuses on tip-match, failing toward not paying a seller that did the work.
⚠ There is no observed field instance. This is a code fact from reading both dedup paths; an earlier "second award event" in this fleet turned out to be an accept event and that attribution was retracted.
Verification
3 passedincluding the neighbour, run under the money-path feature set (--no-default-features --features gateway,git-delivery,wallet).639 passed; 2 ignored(641). This PR adds 2 ⇒ the row should read641 passed; 2 ignored(643 total).cargo test --lib -- --listreports 643 on this branch and both new tests appear by name in it, so they are genuinely in the suite CI runs — not compiled out.money-pathjob.