feat: non-destructive branch recovery after lost issue locks (Closes #440)
Implements the #420 recovery umbrella: keyed persistent issue locks with own-branch adoption, structured branch ownership parsing, create_pr lock resolution after MCP restart, and workflow docs forbidding destructive branch deletion as the default recovery path. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
"""Structured issue/branch ownership evidence (#440).
|
||||
|
||||
Author branches must follow ``(fix|feat|docs|chore)/issue-<n>-<desc>``. Duplicate-work
|
||||
and recovery gates use this parser instead of broad substring checks like
|
||||
``issue-420`` inside unrelated names (for example ``issue-4200``).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
|
||||
IMPLEMENTATION_BRANCH_RE = re.compile(
|
||||
r"^(?:fix|feat|docs|chore)/issue-(\d+)(?:-.+)?$"
|
||||
)
|
||||
|
||||
|
||||
def parse_tracked_issue_number(branch_name: str) -> int | None:
|
||||
"""Return the issue number encoded in a canonical author branch name."""
|
||||
text = (branch_name or "").strip()
|
||||
if not text:
|
||||
return None
|
||||
match = IMPLEMENTATION_BRANCH_RE.match(text)
|
||||
if not match:
|
||||
return None
|
||||
return int(match.group(1))
|
||||
|
||||
|
||||
def branch_tracks_issue(branch_name: str, issue_number: int) -> bool:
|
||||
"""True when ``branch_name`` structurally belongs to ``issue_number``."""
|
||||
parsed = parse_tracked_issue_number(branch_name)
|
||||
return parsed == issue_number if parsed is not None else False
|
||||
Reference in New Issue
Block a user