CallStats gains stall_ms/stall_count (None = no stall observation);
winner sample triple (wall, stall_ms, stall_count) converted once in
_attempt success branch; net = max(0, wall - stall) converged into the
shared _record_winner_sample used by both the single-path guard and
hedge winner registration. Two-fail hedge rounds record nothing
(no winner to attribute). Design section 4.2; issues #27.
Address branch review findings 1-4 on feature/1.3.7-hedged-requests:
- src/polygateway/middleware/retry.py: recheck primary.done() after
hedge admission in _attempt_hedged; release the hedge permit via
settle_and_release(permit, 0) (release_probe for probe entries) and
adjudicate the primary directly instead of firing a billable HTTP
request that would be cancelled immediately
- src/polygateway/config.py: check_hedge_assembly raises a hedge-located
ValueError for empty sources instead of a bare min() error
- tests/unit/test_hedge.py: pin that an injected FakeClock jump of 10^6
seconds does not trigger hedging (design section 8); pin pick(exclude)
counting no gate_rejections and leaving reasons untouched; pin silent
hedge abandonment when the candidate circuit is open; deterministic
regression for the admission-window race (BlockingLimiter harness)
- tests/unit/test_config.py: assert the empty-sources guard message
locates the hedge key
Red-to-green evidence in tests/outputs/137/review-fixes/