Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion skills/fix-plan/scripts/plane_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,11 @@
# plane_create_comment.py, the in-repo precedent that already clears the WAF.
UA = "Mozilla/5.0 (plane-backlog)"

# The identifier prefix is a Plane project identifier, which may contain digits
# (e.g. ES6KR-117). A letters-only prefix class silently matched nothing for such
# workspaces, so every sync reported "0 index lines" instead of failing loudly.
INDEX_LINE_RE = re.compile(
r'^(?P<indent>\s*)-\s+\[(?P<marker>[^\]]*)\]\s+\[(?P<ident>[A-Z]+-\d+)\]\s+(?P<title>.+?)\s+'
r'^(?P<indent>\s*)-\s+\[(?P<marker>[^\]]*)\]\s+\[(?P<ident>[A-Z][A-Z0-9]*-\d+)\]\s+(?P<title>.+?)\s+'
r'(?:→|->)\s+Plane\s+\((?P<url>https://[^\s)]+)\)(?P<rest>.*)$'
)

Expand Down
6 changes: 6 additions & 0 deletions tests/test_plane_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,12 @@ def test_k3s_fallback_script_survives_quote_in_workspace_slug(monkeypatch):
plane_create_issue = importlib.util.module_from_spec(spec)
spec.loader.exec_module(plane_create_issue)

# create_via_k3s_fallback() returns before subprocess.run() when kubectl is
# absent from PATH. Without this stub the test asserts on ambient tooling
# rather than on the escaping behaviour it is about, and fails on any host
# (or git hook with a trimmed PATH) that has no kubectl installed.
monkeypatch.setattr(plane_create_issue.shutil, "which", lambda _cmd: "/usr/bin/kubectl")

captured_cmd = {}

class _FakeCompletedProcess:
Expand Down
26 changes: 26 additions & 0 deletions tests/test_plane_script_defects.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,29 @@ def test_k3s_fallback_skips_gracefully_without_kubectl(script_path, monkeypatch)
assert res["success"] is False
assert "kubectl" in res["reason"]
assert not ran, "fallback must not shell out when kubectl is absent"


def test_index_line_re_matches_identifier_prefix_containing_digits():
"""A Plane project identifier may contain digits (e.g. ES6KR-117).

INDEX_LINE_RE previously matched the prefix with `[A-Z]+`, so no index line
from such a workspace ever matched. plane_sync then reported "0 index lines"
and every marker stayed unsynced — a silent no-op rather than a loud error.
"""
mod = load_module(FIX_PLAN_SCRIPTS / "plane_sync.py", "plane_sync_ident")
url = (
"https://plane.example.invalid/acme/projects/"
"4b4d8bfc-5e5d-495b-bd4c-301fe89e5bb0/issues/"
"016a702b-11aa-424d-834c-53a818e9de0c"
)

for ident in ("ES6KR-117", "INFRA-12", "A1-3"):
line = f"- [ ] [{ident}] some tracked item → Plane ({url})"
match = mod.INDEX_LINE_RE.match(line)
assert match, f"index line with identifier {ident} must match"
assert match.group("ident") == ident

# The prefix must still start with a letter — a purely numeric prefix is not
# a Plane identifier and must not be absorbed.
numeric = f"- [ ] [123-4] not an identifier → Plane ({url})"
assert mod.INDEX_LINE_RE.match(numeric) is None
Loading