feat: harden issue lock store against concurrent session clobbering (Closes #438)

Replace read-modify-write on the global /tmp/gitea_issue_lock.json pointer with
atomic per-issue locks under GITEA_ISSUE_LOCK_DIR using flock serialization,
PID/session metadata, fail-closed stale recovery, and pre-mutation freshness
re-checks in gitea_create_pr. Queue inventory now surfaces live_issue_locks.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
2026-07-07 17:21:53 -04:00
co-authored by Claude Opus 4.8
parent cc4741a4ce
commit 3ad7547a3f
7 changed files with 837 additions and 54 deletions
+5
View File
@@ -13,6 +13,7 @@ import subprocess
from typing import Any
from reviewer_worktree import parse_dirty_tracked_files
import issue_lock_store
PROTECTED_BRANCHES = frozenset({"master", "main", "dev"})
ISSUE_LOCK_FILE = os.environ.get("GITEA_ISSUE_LOCK_FILE", "/tmp/gitea_issue_lock.json")
@@ -49,6 +50,10 @@ def read_issue_lock(path: str | None = None) -> dict[str, Any] | None:
def has_active_issue_lock(branch: str, lock_path: str | None = None) -> bool:
lock = issue_lock_store.resolve_lock_for_branch(branch)
if lock and (lock.get("branch_name") or "").strip() == (branch or "").strip():
freshness = issue_lock_store.assess_lock_freshness(lock)
return freshness["live"]
lock = read_issue_lock(lock_path)
if not lock:
return False