afda1ab5f8
Closes the cross-driver claim-collision race. Forgejo's label-add
endpoint is idempotent — two concurrent dispatchers (impl ↔ rev,
or cross-host instances) can both come back HTTP 200 even though
only one was actually first. The single-instance fcntl lock
prevents same-driver races but cross-driver / cross-host races
remain. conflict_drive.py has had an opt-in mitigation for this
since plan § 3.3.1; this commit ports the simpler "different
auto/claimed-* label appeared in the race window" version into
_dispatch_runtime.dispatch_one so the implementer and reviewer
drivers benefit from the same protection.
Flag-gated via DISPATCHER_VERIFY_CLAIM_AFTER_APPLY=1, default OFF
— preserves today's accept-the-race behaviour byte-for-byte. When
ON:
1. claim_work_item succeeds → label attached.
2. Re-GET /issues/{N}/labels. If a different auto/claimed-* label
is present alongside ours, release our claim (with detail
"post-claim-verify") and return terminal_state="claim-collision".
3. Otherwise register the in-flight claim and proceed normally.
Transient GET failures don't trigger collision (defensive: rather
miss one race than spuriously abandon a healthy claim — the
cycle-failure-budget catches persistent fetch issues).
5 new tests: flag default OFF, the detector's three behaviours
(foreign label → collision, own only → no collision, fetch
failure → no collision), end-to-end dispatch_one releasing on
collision without spawning the worker.
Refs: docs/development/final-working-harvest-plan.md (W5).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>