fix(mcp): forward worktree_path into publication preflight (Closes #815)
gitea_publish_unpublished_issue_branch accepted a required worktree_path but resolved it only after verify_preflight_purity had already run, so the #618 branches-only guard and every other workspace-resolution layer behind preflight received None and fell back to the MCP process root. A daemon rooted at the stable control checkout therefore refused a valid registered issue worktree the caller had explicitly supplied, before the publication assessor could use it — the sole verify_preflight_purity call site that accepted a worktree argument and dropped it. Resolve the workspace once, before preflight, and forward it. A blank or absent path forwards None and keeps the ordinary #618 fail-closed fallback, so guard strictness is unchanged for missing, empty, unregistered, foreign, or control-checkout worktrees. Public tool contract, ownership, cleanliness, hash, ancestry, and read-after-write protections from PR #814 are untouched. Adds tests/test_issue_815_preflight_worktree_forwarding.py: a #735-style capture proving the argument reaches verify_preflight_purity, a faithful production reproduction (control-rooted daemon, no session lock, explicit worktree) that clears #618 on the fixed source and is trapped at #618 on the unpatched source, an end-to-end control-rooted publication in the real topology (PROJECT_ROOT is the stable control checkout, the issue worktree is a distinct registered path, production guards forced on), and negative coverage keeping every #618 and assessor refusal intact. The prior #812 suite masked the defect by patching PROJECT_ROOT to equal the issue worktree. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
+18
-2
@@ -9173,11 +9173,27 @@ def gitea_publish_unpublished_issue_branch(
|
||||
if blocked:
|
||||
return blocked
|
||||
|
||||
verify_preflight_purity(remote, task=task, org=org, repo=repo)
|
||||
# #815: resolve the caller's worktree *before* preflight and forward it, so
|
||||
# every workspace-resolution layer behind verify_preflight_purity — including
|
||||
# the #618 branches-only guard — judges the registered issue worktree this
|
||||
# publication actually operates on. Resolving it afterwards let preflight
|
||||
# fall back to the MCP process root, so a daemon rooted at the stable control
|
||||
# checkout refused a valid explicit worktree before the assessor ever ran.
|
||||
# A caller supplying nothing usable forwards None and keeps the ordinary
|
||||
# fail-closed fallback.
|
||||
explicit_worktree = (worktree_path or "").strip()
|
||||
workspace = os.path.realpath(os.path.abspath(explicit_worktree or "."))
|
||||
|
||||
verify_preflight_purity(
|
||||
remote,
|
||||
worktree_path=workspace if explicit_worktree else None,
|
||||
task=task,
|
||||
org=org,
|
||||
repo=repo,
|
||||
)
|
||||
|
||||
h, o, r = _resolve(remote, host, org, repo)
|
||||
git_remote = (git_remote_name or remote or "").strip()
|
||||
workspace = os.path.realpath(os.path.abspath((worktree_path or "").strip() or "."))
|
||||
|
||||
existing_lock = issue_lock_store.load_issue_lock(
|
||||
remote=remote, org=o, repo=r, issue_number=int(issue_number)
|
||||
|
||||
Reference in New Issue
Block a user