fix: preserve actual reconciler role for comment_issue preflight (Closes #540)

Merge PR #541 — preserve actual reconciler role for #274/#475 exemptions during comment_issue preflight (Closes #540).
This commit was merged in pull request #541.
This commit is contained in:
2026-07-08 21:09:33 -05:00
3 changed files with 323 additions and 4 deletions
+35 -2
View File
@@ -211,6 +211,28 @@ def _effective_workspace_role() -> str:
)
def _actual_profile_role() -> str:
"""Resolve the workspace role from the ACTIVE PROFILE alone (#540).
Unlike :func:`_effective_workspace_role`, this never consults
``_preflight_resolved_role``. A task capability whose ``required_role_kind``
is ``author`` (e.g. ``comment_issue``) stamps ``_preflight_resolved_role =
"author"``; the #274/#475 role exemptions must key off the real profile
identity so that stamp cannot poison a genuine reviewer/merger/reconciler
session into being treated as an author. Author profiles still classify as
``author`` here, so author blocking is preserved.
"""
profile = get_profile()
role = _role_kind(
profile.get("allowed_operations") or [],
profile.get("forbidden_operations") or [],
)
return nwb.normalize_role_kind(
role,
profile_name=profile.get("profile_name"),
)
def _resolve_preflight_workspace_path(worktree_path: str | None = None) -> str:
"""Resolve the namespace-scoped workspace root inspected by pre-flight guards."""
role = _effective_workspace_role()
@@ -541,9 +563,19 @@ def _enforce_branches_only_author_mutation(worktree_path: str | None = None) ->
Reviewer, merger, and reconciler roles are exempt: reconciler ``close_pr``
is a Gitea metadata mutation and must not require ``GITEA_AUTHOR_WORKTREE``
(#468). Non-author namespaces use dedicated workspace env vars (#510).
The exemption honours BOTH the effective workspace role and the actual
profile role (#540). ``comment_issue`` preflight stamps
``_preflight_resolved_role = "author"`` (its ``required_role_kind``), which
would otherwise poison :func:`_effective_workspace_role` into classifying a
genuine reconciler as an author and defeat this exemption. Keying off the
real profile role as well preserves the exemption without weakening author
blocking an actual author profile classifies as ``author`` in both.
"""
role = _effective_workspace_role()
if role in nwb.NON_AUTHOR_ROLES:
if (
_effective_workspace_role() in nwb.NON_AUTHOR_ROLES
or _actual_profile_role() in nwb.NON_AUTHOR_ROLES
):
return
ctx = _resolve_namespace_mutation_context(worktree_path)
workspace = ctx["workspace_path"]
@@ -710,6 +742,7 @@ def _enforce_root_checkout_guard(worktree_path: str | None = None) -> None:
porcelain_status=git_state.get("porcelain_status") or "",
remote_master_sha=remote_master_sha,
resolved_role=_preflight_resolved_role,
actual_role=_actual_profile_role(),
)
if assessment["block"]:
raise RuntimeError(root_checkout_guard.format_root_checkout_guard_error(assessment))