From 472e6850fe8a9b31728a917ccbbf04bd3aace4d1 Mon Sep 17 00:00:00 2001 From: Jason Walker <913443@dadeschools.net> Date: Thu, 2 Jul 2026 19:46:21 -0400 Subject: [PATCH] fix: close implements tracker gap and clarify closing keywords (#110) --- docs/llm-workflow-runbooks.md | 2 +- mcp_server.py | 2 +- skills/llm-project-workflow/SKILL.md | 4 ++-- .../templates/merge-pr.md | 2 +- .../templates/review-pr.md | 2 +- .../templates/start-issue.md | 11 +++++----- tests/test_mcp_server.py | 20 +++++++++++++++++++ 7 files changed, 32 insertions(+), 11 deletions(-) diff --git a/docs/llm-workflow-runbooks.md b/docs/llm-workflow-runbooks.md index 57a0cd3..90d9329 100644 --- a/docs/llm-workflow-runbooks.md +++ b/docs/llm-workflow-runbooks.md @@ -227,7 +227,7 @@ tied to an issue number so the work is traceable end to end: | Issue | `#123` (claimed with `status:in-progress`) | | Branch | `(fix\|feat\|docs\|chore)/issue-123-` (review: `review/pr-456-`) | | Worktree | `branches/fix-issue-123-` (slashes → hyphens) | -| PR | body says `Closes #123` (closes) or `Refs #123` (related) | +| PR | body says `Closes #123` or `Fixes #123` (closes issue); `Implements #123` or `Refs #123` (does NOT close) | | Cleanup | remove remote+local branch + worktree folder; drop `status:in-progress` | `scripts/worktree-start` **rejects** implementation branches that are not diff --git a/mcp_server.py b/mcp_server.py index ae06038..a26d510 100644 --- a/mcp_server.py +++ b/mcp_server.py @@ -53,7 +53,7 @@ mcp = FastMCP("gitea-tools", instructions=( def extract_linked_issue_numbers(text: str | None, branch_name: str | None = None) -> list[int]: issues = set() if text: - pattern = re.compile(r'(?i)(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?|ref[s]?)\s+#(\d+)') + pattern = re.compile(r'(?i)(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?|implement[s]?|implemented)\s+#(\d+)') issues.update(int(m) for m in pattern.findall(text)) if branch_name: pattern = re.compile(r'(?i)issue-(\d+)') diff --git a/skills/llm-project-workflow/SKILL.md b/skills/llm-project-workflow/SKILL.md index 6b94412..8b48437 100644 --- a/skills/llm-project-workflow/SKILL.md +++ b/skills/llm-project-workflow/SKILL.md @@ -90,8 +90,8 @@ is maintained by: - the branch name (contains the issue number), - a claim comment on the issue, e.g. `Claimed. Branch: fix/issue-123-short-description. Worktree: branches/fix-issue-123-short-description.`, -- the PR body — `Closes #123` when the PR should close the issue, `Refs #123` - when related but not closing, +- the PR body — `Closes #123` or `Fixes #123` when the PR should close the issue + (do NOT use `Implements #123` or `Refs #123` to close, as Gitea will not auto-close), - cleanup after merge — remove the remote branch, local branch, and the issue worktree folder, and drop `status:in-progress`. diff --git a/skills/llm-project-workflow/templates/merge-pr.md b/skills/llm-project-workflow/templates/merge-pr.md index 5d7266d..6c61bb5 100644 --- a/skills/llm-project-workflow/templates/merge-pr.md +++ b/skills/llm-project-workflow/templates/merge-pr.md @@ -19,7 +19,7 @@ Steps: - Target task role: merger identity (must NOT be the PR author) *If the current identity does not match the required role (or is the PR author), STOP. Relaunch/switch to the correct profile first.* 2. Verify authenticated identity + active profile. -3. Confirm PR #: author (not you), state open, mergeable, review approved. +3. Confirm PR #: author (not you), state open, mergeable, review approved. Check if PR body uses `Closes #N` or `Fixes #N`; if it uses `Implements #N` or `Refs #N`, manual closing will be needed in step 29. 4. If any gate fails → STOP and report. 4. Merge with explicit confirmation (e.g. confirmation="MERGE PR "), optionally pinning the reviewed head SHA / changed-file set. diff --git a/skills/llm-project-workflow/templates/review-pr.md b/skills/llm-project-workflow/templates/review-pr.md index 778b835..047f843 100644 --- a/skills/llm-project-workflow/templates/review-pr.md +++ b/skills/llm-project-workflow/templates/review-pr.md @@ -24,7 +24,7 @@ Steps: 4. scripts/worktree-review # detached, branches/review-* cd branches/review- 5. Confirm the worktree is clean. Inspect the FULL diff; confirm scope matches - issue #; flag any unrelated files, secrets, or formatting churn. + issue #; flag any unrelated files, secrets, or formatting churn. Check that the PR body correctly uses Gitea-closing keywords (`Closes #N` or `Fixes #N`) instead of non-closing ones (`Implements #N`, `Refs #N`). 6. Run the test suite; note results. 7. Post the review verdict: approve only if scope is clean and checks pass; otherwise request changes with specifics. Never merge from this review step. diff --git a/skills/llm-project-workflow/templates/start-issue.md b/skills/llm-project-workflow/templates/start-issue.md index 58d982e..dc2dd6c 100644 --- a/skills/llm-project-workflow/templates/start-issue.md +++ b/skills/llm-project-workflow/templates/start-issue.md @@ -22,12 +22,13 @@ Steps: 3. git fetch --prune; confirm local master == /master (0 0). 4. Create the issue "" (problem, scope, acceptance) and claim it (status:in-progress + a "starting work" comment naming the branch). -4. scripts/worktree-start <type>/issue-<n>-<slug> # type = fix|feat|docs +5. scripts/worktree-start <type>/issue-<n>-<slug> # type = fix|feat|docs cd branches/<type>-issue-<n>-<slug> -5. Implement the narrow scope only; add/update focused tests if behavior changes. -6. Checks: run the test suite, compile/lint changed files, git diff --check, +6. Implement the narrow scope only; add/update focused tests if behavior changes. +7. Checks: run the test suite, compile/lint changed files, git diff --check, and scan the diff for secrets. -7. Commit (issue-linked message), push the branch, open a PR to master. +8. Commit (issue-linked message), push the branch, open a PR to master. + *The PR body MUST use closing keywords like `Closes #N` or `Fixes #N` to close the issue; do NOT use `Implements #N` or `Refs #N` for closing, as Gitea will not auto-close it.* Include an "LLM Handoff Metadata" block in the PR body (attribution only; never an eligibility input — docs/llm-agent-sha.md): @@ -39,7 +40,7 @@ Steps: - Branch: <branch> - Worktree: <worktree path> - Self-review allowed: no -8. Stop before review/merge — you are the author. +9. Stop before review/merge — you are the author. Handoff: issue #, branch, worktree path, files changed, checks + results, PR URL — formatted as the Controller Handoff Summary (SKILL.md §K); end with diff --git a/tests/test_mcp_server.py b/tests/test_mcp_server.py index 94d4975..dfe5be6 100644 --- a/tests/test_mcp_server.py +++ b/tests/test_mcp_server.py @@ -1649,3 +1649,23 @@ class TestTrackerHygieneCleanup(unittest.TestCase): res = gitea_close_issue(issue_number=1) self.assertTrue(res["success"]) self.assertIn("error:", res["cleanup_status"].get(1)) + + def test_extract_linked_issue_numbers_hygiene(self): + from mcp_server import extract_linked_issue_numbers + # Standard closing keywords + self.assertEqual(extract_linked_issue_numbers("Closes #123"), [123]) + self.assertEqual(extract_linked_issue_numbers("Fixes #123"), [123]) + self.assertEqual(extract_linked_issue_numbers("Resolves #123"), [123]) + + # New implements/implemented keywords + self.assertEqual(extract_linked_issue_numbers("Implements #123"), [123]) + self.assertEqual(extract_linked_issue_numbers("implemented #123"), [123]) + self.assertEqual(extract_linked_issue_numbers("implement #123"), [123]) + + # refs / ref should NOT match + self.assertEqual(extract_linked_issue_numbers("Refs #123"), []) + self.assertEqual(extract_linked_issue_numbers("ref #123"), []) + + # branch name fallback + self.assertEqual(extract_linked_issue_numbers("", branch_name="issue-123"), [123]) + self.assertEqual(extract_linked_issue_numbers("", branch_name="feat/issue-123-foo"), [123])