From dbcf2abc147437dfd5cf4d7a4139e37305f0db8e Mon Sep 17 00:00:00 2001 From: DrumRobot Date: Sun, 23 Aug 2026 01:15:56 +0900 Subject: [PATCH 1/2] fix(fix-plan): accept digit-bearing Plane identifiers in INDEX_LINE_RE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit INDEX_LINE_RE matched the identifier prefix with [A-Z]+, so an index line from a workspace whose project identifier contains a digit (ES6KR-117) never matched. plane_sync then reported "0 index lines" and left every marker unsynced — a silent no-op rather than a loud failure, which is why the gap went unnoticed. Widen the prefix to [A-Z][A-Z0-9]* (still letter-initial, so a purely numeric [123-4] is not absorbed) and cover both directions with a regression test. --- skills/fix-plan/scripts/plane_sync.py | 5 ++++- tests/test_plane_script_defects.py | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/skills/fix-plan/scripts/plane_sync.py b/skills/fix-plan/scripts/plane_sync.py index 95012dc8..3d52a437 100644 --- a/skills/fix-plan/scripts/plane_sync.py +++ b/skills/fix-plan/scripts/plane_sync.py @@ -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\s*)-\s+\[(?P[^\]]*)\]\s+\[(?P[A-Z]+-\d+)\]\s+(?P.+?)\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>.*)$' ) diff --git a/tests/test_plane_script_defects.py b/tests/test_plane_script_defects.py index 7607ae2b..31934ae5 100644 --- a/tests/test_plane_script_defects.py +++ b/tests/test_plane_script_defects.py @@ -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 From c9147b6b14390007301320875730d5557e32f4c6 Mon Sep 17 00:00:00 2001 From: DrumRobot <drumrobot43@gmail.com> Date: Sun, 23 Aug 2026 01:16:04 +0900 Subject: [PATCH 2/2] fix(tests): stub kubectl lookup in the k3s fallback escaping test create_via_k3s_fallback() returns before subprocess.run() when kubectl is absent from PATH, so the escaping test asserted on ambient tooling rather than on the behaviour it names. It failed in a pre-push run whose hook environment had no /usr/local/bin, and would fail on any CI host without kubectl installed. Stub shutil.which the same way test_k3s_fallback_skips_gracefully_without_kubectl already does, so the test exercises the escaping path unconditionally. --- tests/test_plane_profile.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test_plane_profile.py b/tests/test_plane_profile.py index 14bbeca4..86c857e5 100644 --- a/tests/test_plane_profile.py +++ b/tests/test_plane_profile.py @@ -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: