fix: preserve actual reconciler role for #274/#475 role exemptions during comment_issue preflight (Closes #540) #541
Labels
Clear labels
allocator
anti-stomp
architecture
bug
chore
codex
concurrency
contamination
control-plane
dashboard
database
design
documentation
enhancement
gitea
glitchtip
important
incident
incident-bridge
integration
jenkins
labels
leases
mcp
mcp-health
mcp-menu
multi-project
mutating
nice-to-have
observability
portability
preflight
protected-branch
queue
read-only
reconnect
recovery
refactor
release
reliability
resumable-review
reviewer
roadmap
safety
security
self-hosted
sentry
stale-runtime
status:blocked
status:in-progress
status:pr-open
status:ready
terminal-lock
testing
tracker
type:bug
type:feature
type:feature
type:guardrail
visibility
workflow
workflow-hardening
workflow-hardening
Controller-owned work allocator
Prevent concurrent LLM session stomping
Architecture / structural design
OpenAI Codex client / workflow session surface
Concurrent session safety
Workflow or session contamination incident
MCP control-plane coordination and allocation authority
MCP operational dashboard/queue view
Internal coordination storage (SQLite/Postgres)
Design / investigation, no implementation
Docs / runbooks
New feature or improvement
Gitea MCP workflow
GlitchTip integration
Operational or process incident requiring durable audit trail
Sentry-to-Gitea incident bridging
Integration testing
Jenkins integration
Label taxonomy management
Lease adopt/release/expire lifecycle
MCP server / tooling
MCP namespace and runtime health
MCP menu surface
Work spanning multiple monitoring projects or Gitea repos
Mutating action; requires gating
Observability, metrics, traces, error reporting
Cross-platform / portability
Shared preflight gates before mutation
Protected branch / stable-branch policy concern
Work queue visibility and allocation
Read-only, no mutation
MCP client reconnect/reload recovery path
Recovery paths for stale/foreign leases
Code refactor / restructure
Release / versioning
Reliability / failure handling
Persist and resume prepared review verdicts across sessions
Reviewer workflow tooling
Roadmap / umbrella issue
Safety rails and fail-closed mutation guards
Security / trust boundary
Self-hosted infrastructure integration
Sentry error monitoring integration
Stale backend daemon / runtime-vs-master parity failures
Issue is blocked
Issue is being worked on
Issue has an open pull request
Issue is ready for work
Terminal review lock (#332) path
Tests / test coverage
Issue tracker hygiene / meta
Bug or defect
Feature or enhancement
Feature or enhancement
Safety gate or guardrail
Workflow state visibility for LLMs/operators
Cross-tool workflow
LLM workflow coordination hardening
LLM workflow coordination hardening
No labels
Milestone
No items
No Milestone
Projects
Clear projects
No projects
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: Scaled-Tech-Consulting/Gitea-Tools#541
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #540.
Root cause
resolve_task_capability("comment_issue")stamps_preflight_resolved_role = "author"becausecomment_issue.required_role_kind = author._effective_workspace_role()prefers that stamp over the active profile role, so a genuineprgs-reconcilersession is classified as an author inside:_enforce_branches_only_author_mutation(#274 branch-only mutation guard), and_enforce_root_checkout_guard->assess_root_checkout_guard(#475 root checkout guard).Both then treated the reconciler as an author, blocking
gitea_create_issue_commentfrom the control checkout even though the reconciler profile holdsgitea.issue.comment.Fix
Key the role exemptions off the actual active profile role as well as the effective workspace role, so the poisoned
authortask stamp cannot strip a non-author profile of its exemption:_actual_profile_role()— derives the workspace role from the active profile alone, never from_preflight_resolved_role._enforce_branches_only_author_mutation— exempt when either the effective role or the actual profile role is a non-author role._enforce_root_checkout_guard— passesactual_role;assess_root_checkout_guardgains anactual_roleparam and exempts a reconciler by resolved or actual role.Preserved behavior:
authorin both signals, so it stays blocked on a contaminated control checkout by #274/#475.actual_role), so a merger operating from its cleanbranches/workspace under a non-merge task is not over-tightened (a regression caught mid-implementation and corrected).Files changed
gitea_mcp_server.py— add_actual_profile_role(); use actual-role exemption in_enforce_branches_only_author_mutation; passactual_rolefrom_enforce_root_checkout_guard.root_checkout_guard.py—assess_root_checkout_guardgainsactual_role; reconciler exemption honours resolved or actual role; merger strictness unchanged.tests/test_issue_540_comment_role_poison.py— new regression suite.Regression tests added
tests/test_issue_540_comment_role_poison.py:_actual_profile_role()returns the real role (reconciler/author/reviewer) even when_preflight_resolved_role="author", while_effective_workspace_role()stays poisoned (documents the mechanism)._enforce_branches_only_author_mutationexempts a reconciler under the poisoned author stamp and does not exempt an actual author.assess_root_checkout_guard— actual-reconciler exempt despite poisoned resolvedauthor; actual-author still blocked on contaminated root; merger strictness keyed on resolved task role;actual_role="merger"under a non-merge task does not over-tighten; backward compatible withoutactual_role.gitea_create_issue_comment: reconciler comment from the control checkout succeeds; author comment from the control checkout is blocked.Validation results
tests/test_issue_540_comment_role_poison.py— 12 passed.test_verify_preflight_blocks_control_checkout_with_test_porcelain,test_pr487_style_merge_binds_clean_merger_workspace) are pre-existing on clean master — verified by stashing this change and re-running, they fail identically. They are outside the scope of #540 and are not touched by this PR.git diff --check— clean.Mutation ledger
git worktreebranches/issue-540-reconciler-comment-role-poisonoffmaster(cfce823) for edits, plus a master-pinnedbranches/issue-540-lock-basefor the durable lock. Committed + pushed branchfeat/issue-540-reconciler-comment-role-poison(head9438855). Root checkout left clean onmaster.branches/mcp-author-worktree(foreign staged work) untouched.Follow-up
The PR #527 / issue #515 canonical reconciler audit comment remains blocked until #540 lands. After this merges, a reconciler session should retry only that single blocked canonical audit comment — no other reconciliation action is outstanding (post-merge moot lease cleanup already proven a no-op).
resolve_task_capability("comment_issue") stamps _preflight_resolved_role = "author" because comment_issue.required_role_kind = author. _effective_workspace_role() prefers that stamp, so a genuine prgs-reconciler session was classified as an author inside the #274 branch-only mutation guard and the #475 root checkout guard, blocking gitea_create_issue_comment from the control checkout. Fix keys the role exemptions off the actual active profile role as well as the effective workspace role: - Add _actual_profile_role(): derives the workspace role from the active profile alone, never from _preflight_resolved_role. - _enforce_branches_only_author_mutation: exempt when EITHER the effective role OR the actual profile role is a non-author role. - _enforce_root_checkout_guard: pass actual_role; assess_root_checkout_guard now exempts a reconciler by resolved OR actual role. Author blocking is preserved: an actual author profile classifies as author in both signals, so it stays blocked on a contaminated control checkout. Merger strictness stays keyed on the resolved task role so a merger operating from its clean branches/ workspace under a non-merge task is not over-tightened. Regression tests (tests/test_issue_540_comment_role_poison.py) cover the reconciler comment path, author-blocking path, reviewer/merger/reconciler role classification under a poisoned author stamp, and the root guard actual_role exemption. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #541
issue: #540
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 71026-a73bae0ae00d
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr541
phase: claimed
candidate_head:
9438855fddtarget_branch: master
target_branch_sha:
cfce823dd7last_activity: 2026-07-08T16:50:05Z
expires_at: 2026-07-08T18:50:05Z
blocker: none
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #541
issue: #540
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 71026-a73bae0ae00d
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr541
phase: claimed
candidate_head:
9438855fddtarget_branch: master
target_branch_sha:
cfce823dd7last_activity: 2026-07-08T16:54:05Z
expires_at: 2026-07-08T18:54:05Z
blocker: none
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #541
issue: #540
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 34817-3c5dd715caff
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr541-215611
phase: claimed
candidate_head:
9438855fddtarget_branch: master
target_branch_sha:
18e7d7b6e1last_activity: 2026-07-09T01:57:03Z
expires_at: 2026-07-09T03:57:03Z
blocker: none
Independent review: APPROVE (PR #541 / Issue #540)
Scope
Fix role-poison from
comment_issuepreflight stamping_preflight_resolved_role=author, which defeated reconciler exemptions on #274 branch-only and #475 root-checkout guards.Files (domain-restricted)
gitea_mcp_server.py—_actual_profile_role(); #274 exemption OR on actual profile role; passactual_roleinto root guardroot_checkout_guard.py— reconciler exempt if resolved or actual role is reconciler; merger strictness stays on resolved task roletests/test_issue_540_comment_role_poison.py— new regression suiteValidation (isolated worktree
branches/review-pr541-215611)9438855fdd135392b8b8369929efe03b32d9da31master(live tip at lease time18e7d7b); Gitea mergeable: truegit diff --check prgs/master...HEAD: cleantest_issue_540_comment_role_poison+test_root_checkout_guard— 23 passedtest_verify_preflight_blocks_control_checkout_with_test_porcelaintest_pr487_style_merge_binds_clean_merger_workspaceDesign notes reviewed
actual_role(avoids over-tightening non-merge tasks from merger workspace)Verdict
APPROVE. Ready for independent merger.
LLM-Role: reviewer
Authenticated-Gitea-User: sysadmin
MCP-Profile: prgs-reviewer
Self-merge: no
adopted_at: 2026-07-09T02:09:23Z
adopted_by_identity: sysadmin
adopted_by_profile: prgs-merger
adopted_from_session_id: 34817-3c5dd715caff
adopted_from_profile: prgs-reviewer
adopted_from_reviewer_identity: sysadmin
adopted_from_comment_id: 7317
adoption_reason: merger-handoff-approved-head
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #541
issue: #540
reviewer_identity: sysadmin
profile: prgs-merger
session_id: 34819-e70c03aa27d4
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/merge-pr541
phase: adopted
candidate_head:
9438855fddtarget_branch: master
target_branch_sha:
18e7d7b6e1last_activity: 2026-07-09T02:09:23Z
expires_at: 2026-07-09T04:09:23Z
blocker: none