feat: isolate MCP workspace binding across role namespaces (Closes #510) #512

Merged
sysadmin merged 1 commits from feat/issue-510-workspace-binding-isolation into master 2026-07-08 03:37:18 -05:00
Owner

Closes #510

Summary

Isolates MCP workspace binding per role namespace so GITEA_AUTHOR_WORKTREE cannot poison reviewer, merger, or reconciler workspace purity checks.

Changes

  • namespace_workspace_binding.py — role-scoped resolution (GITEA_AUTHOR_WORKTREE, GITEA_REVIEWER_WORKTREE, GITEA_MERGER_WORKTREE, GITEA_RECONCILER_WORKTREE), foreign-binding detection, canonical error messages with binding source and remediation
  • gitea_mcp_server.py — namespace-aware preflight/workspace guards; _verify_role_mutation_workspace for reviewer/merger mutations; worktree_path on merge/review tools
  • docs/llm-workflow-runbooks.md — namespace binding table, reconnect procedure, CTH guidance
  • tests/test_namespace_workspace_binding.py — acceptance criteria coverage

#274 / #475 preserved

  • Author mutations still require branches/ worktrees (#274)
  • Stable control checkout still blocks author mutations
  • Dirty active task workspaces still fail closed
  • Root-control protections unchanged

Validation

pytest tests/test_namespace_workspace_binding.py tests/test_workspace_guard_alignment.py tests/test_create_issue_workspace_guard.py tests/test_reconciler_close_workspace_guard.py tests/test_mcp_server.py::TestPreflightVerification -q
# 37 passed

pytest tests/ -q
# 1 failed (pre-existing: test_merge_blocked_on_stale_approval_head), 1788 passed

py_compile namespace_workspace_binding.py gitea_mcp_server.py
git diff --check

Worktree

/Users/jasonwalker/Development/Gitea-Tools/branches/issue-510-workspace-binding-isolation @ f1ad665

Closes #510 ## Summary Isolates MCP workspace binding per role namespace so `GITEA_AUTHOR_WORKTREE` cannot poison reviewer, merger, or reconciler workspace purity checks. ## Changes - `namespace_workspace_binding.py` — role-scoped resolution (`GITEA_AUTHOR_WORKTREE`, `GITEA_REVIEWER_WORKTREE`, `GITEA_MERGER_WORKTREE`, `GITEA_RECONCILER_WORKTREE`), foreign-binding detection, canonical error messages with binding source and remediation - `gitea_mcp_server.py` — namespace-aware preflight/workspace guards; `_verify_role_mutation_workspace` for reviewer/merger mutations; `worktree_path` on merge/review tools - `docs/llm-workflow-runbooks.md` — namespace binding table, reconnect procedure, CTH guidance - `tests/test_namespace_workspace_binding.py` — acceptance criteria coverage ## #274 / #475 preserved - Author mutations still require `branches/` worktrees (#274) - Stable control checkout still blocks author mutations - Dirty active task workspaces still fail closed - Root-control protections unchanged ## Validation ```bash pytest tests/test_namespace_workspace_binding.py tests/test_workspace_guard_alignment.py tests/test_create_issue_workspace_guard.py tests/test_reconciler_close_workspace_guard.py tests/test_mcp_server.py::TestPreflightVerification -q # 37 passed pytest tests/ -q # 1 failed (pre-existing: test_merge_blocked_on_stale_approval_head), 1788 passed py_compile namespace_workspace_binding.py gitea_mcp_server.py git diff --check ``` ## Worktree `/Users/jasonwalker/Development/Gitea-Tools/branches/issue-510-workspace-binding-isolation` @ `f1ad665`
jcwalker3 added 1 commit 2026-07-08 03:08:13 -05:00
Namespace-scoped workspace resolution prevents GITEA_AUTHOR_WORKTREE from
poisoning reviewer, merger, and reconciler purity checks. Each role uses
its own worktree env var with actionable binding-source errors and safe
reconnect guidance. Preserves #274/#475 author and control-checkout guards.
jcwalker3 force-pushed feat/issue-510-workspace-binding-isolation from f1ad665242 to 3d11e1f12b 2026-07-08 03:08:13 -05:00 Compare
Owner

repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #512
issue: #510
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 99141-ac1abd572f02
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr512-submit
phase: claimed
candidate_head: 3d11e1f12b
target_branch: master
target_branch_sha: none
last_activity: 2026-07-08T08:25:43Z
expires_at: 2026-07-08T10:25:43Z
blocker: none

<!-- mcp-review-lease:v1 --> repo: Scaled-Tech-Consulting/Gitea-Tools pr: #512 issue: #510 reviewer_identity: sysadmin profile: prgs-reviewer session_id: 99141-ac1abd572f02 worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr512-submit phase: claimed candidate_head: 3d11e1f12b659bf28e8dde06e1ca73268786197b target_branch: master target_branch_sha: none last_activity: 2026-07-08T08:25:43Z expires_at: 2026-07-08T10:25:43Z blocker: none
sysadmin approved these changes 2026-07-08 03:26:02 -05:00
sysadmin left a comment
Owner

Review report — PR #512 / issue #510

Verdict: APPROVED @ 3d11e1f12b659bf28e8dde06e1ca73268786197b

Identity / profile proof

  • MCP namespace: gitea-reviewer
  • Profile: prgs-reviewer
  • Authenticated user: sysadmin (user_id 4)
  • Task capability: review_pr allowed

Self-review proof

  • PR author: jcwalker3
  • Reviewer: sysadminnot self-review (self_author: false)

PR gate proof

  • PR #512: open, mergeable: true
  • Head SHA reviewed: 3d11e1f12b659bf28e8dde06e1ca73268786197b (matches expected)
  • Branch: feat/issue-510-workspace-binding-isolation
  • Review worktree: branches/review-pr512-submit (detached @ head; author worktrees untouched)

Scope check (7 files, +776/-47)

  • namespace_workspace_binding.py — role-scoped env resolution; foreign-binding detection; actionable errors
  • gitea_mcp_server.py — namespace-aware preflight; _verify_role_mutation_workspace(task=...) wired for review/merge/lease paths; #502 task mismatch preserved
  • tests/test_namespace_workspace_binding.py — AC coverage for cross-namespace isolation
  • tests/test_mcp_server.py — preflight error format updates
  • docs/llm-workflow-runbooks.md, .env.example, author_mutation_worktree.py comment
  • No unrelated files; no authorization widening observed

Review focus findings

  1. #510 isolation: GITEA_AUTHOR_WORKTREE ignored for reviewer/merger/reconciler; per-role env vars used.
  2. #502 regression check: Rebase conflict resolution correctly passes task="review_pr" / task="merge_pr" through _verify_role_mutation_workspaceverify_preflight_purity.
  3. #274/#475 preserved: Author still requires branches/ worktree; dirty active workspace still fails closed; non-author merger/reconciler may use clean control checkout.
  4. PR #487 scenario: Merger with dirty author env + clean merger/control workspace no longer blocked by foreign author WIP.
  5. Minor note (non-blocking): gitea_heartbeat_reviewer_pr_lease still calls verify_preflight_purity(task="review_pr") directly; acceptable because namespace-scoped resolution already applies at that layer.

Validation

Check Result
py_compile (changed Python) passed
Targeted tests (namespace + preflight survival + workspace guards + preflight verification) 42 passed
Full suite 1793 passed, 1 pre-existing failure (test_merge_blocked_on_stale_approval_head)
git diff --check prgs/master...HEAD passed
Secret/provenance scan on diff no new secrets
Dry-run approve would_perform: true @ pinned head

Mutation ledger

  • gitea_whoami (read)
  • gitea_resolve_task_capability(task=review_pr) (read)
  • gitea_view_pr / gitea_check_pr_eligibility (read)
  • gitea_acquire_reviewer_pr_lease (lease @ branches/review-pr512-submit)
  • gitea_dry_run_pr_review(action=approve) (dry-run)
  • gitea_mark_final_review_decision(action=approve) (session state)
  • gitea_submit_pr_review(action=approve) (live review mutation)
  • No merge performed
  • No author worktree mutations

Merger handoff (pinned)

After merge, re-test PR #487 merge using merger namespace with clean GITEA_MERGER_WORKTREE or clean control checkout — do not bind via GITEA_AUTHOR_WORKTREE.

## Review report — PR #512 / issue #510 **Verdict: APPROVED** @ `3d11e1f12b659bf28e8dde06e1ca73268786197b` ### Identity / profile proof - MCP namespace: `gitea-reviewer` - Profile: `prgs-reviewer` - Authenticated user: `sysadmin` (user_id 4) - Task capability: `review_pr` allowed ### Self-review proof - PR author: `jcwalker3` - Reviewer: `sysadmin` — **not self-review** (`self_author: false`) ### PR gate proof - PR #512: **open**, **mergeable: true** - Head SHA reviewed: `3d11e1f12b659bf28e8dde06e1ca73268786197b` (matches expected) - Branch: `feat/issue-510-workspace-binding-isolation` - Review worktree: `branches/review-pr512-submit` (detached @ head; author worktrees untouched) ### Scope check (7 files, +776/-47) - `namespace_workspace_binding.py` — role-scoped env resolution; foreign-binding detection; actionable errors - `gitea_mcp_server.py` — namespace-aware preflight; `_verify_role_mutation_workspace(task=...)` wired for review/merge/lease paths; #502 task mismatch preserved - `tests/test_namespace_workspace_binding.py` — AC coverage for cross-namespace isolation - `tests/test_mcp_server.py` — preflight error format updates - `docs/llm-workflow-runbooks.md`, `.env.example`, `author_mutation_worktree.py` comment - **No unrelated files; no authorization widening observed** ### Review focus findings 1. **#510 isolation:** `GITEA_AUTHOR_WORKTREE` ignored for reviewer/merger/reconciler; per-role env vars used. 2. **#502 regression check:** Rebase conflict resolution correctly passes `task="review_pr"` / `task="merge_pr"` through `_verify_role_mutation_workspace` → `verify_preflight_purity`. 3. **#274/#475 preserved:** Author still requires `branches/` worktree; dirty active workspace still fails closed; non-author merger/reconciler may use clean control checkout. 4. **PR #487 scenario:** Merger with dirty author env + clean merger/control workspace no longer blocked by foreign author WIP. 5. **Minor note (non-blocking):** `gitea_heartbeat_reviewer_pr_lease` still calls `verify_preflight_purity(task="review_pr")` directly; acceptable because namespace-scoped resolution already applies at that layer. ### Validation | Check | Result | |-------|--------| | `py_compile` (changed Python) | passed | | Targeted tests (namespace + preflight survival + workspace guards + preflight verification) | **42 passed** | | Full suite | **1793 passed**, 1 pre-existing failure (`test_merge_blocked_on_stale_approval_head`) | | `git diff --check prgs/master...HEAD` | passed | | Secret/provenance scan on diff | no new secrets | | Dry-run approve | `would_perform: true` @ pinned head | ### Mutation ledger - `gitea_whoami` (read) - `gitea_resolve_task_capability(task=review_pr)` (read) - `gitea_view_pr` / `gitea_check_pr_eligibility` (read) - `gitea_acquire_reviewer_pr_lease` (lease @ `branches/review-pr512-submit`) - `gitea_dry_run_pr_review(action=approve)` (dry-run) - `gitea_mark_final_review_decision(action=approve)` (session state) - `gitea_submit_pr_review(action=approve)` (live review mutation) - **No merge performed** - **No author worktree mutations** ### Merger handoff (pinned) After merge, re-test PR #487 merge using merger namespace with clean `GITEA_MERGER_WORKTREE` or clean control checkout — **do not** bind via `GITEA_AUTHOR_WORKTREE`.
sysadmin merged commit c54ac0d4de into master 2026-07-08 03:37:18 -05:00
Owner

repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #512
issue: #510
reviewer_identity: sysadmin
profile: prgs-reconciler
session_id: 99141-ac1abd572f02
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr512-submit
phase: released
candidate_head: 3d11e1f12b
target_branch: master
target_branch_sha: c54ac0d4de
last_activity: 2026-07-08T08:42:27Z
expires_at: 2026-07-08T10:42:27Z
blocker: none

<!-- mcp-review-lease:v1 --> repo: Scaled-Tech-Consulting/Gitea-Tools pr: #512 issue: #510 reviewer_identity: sysadmin profile: prgs-reconciler session_id: 99141-ac1abd572f02 worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr512-submit phase: released candidate_head: 3d11e1f12b659bf28e8dde06e1ca73268786197b target_branch: master target_branch_sha: c54ac0d4de85d3f6b5246327b3200a6a39282933 last_activity: 2026-07-08T08:42:27Z expires_at: 2026-07-08T10:42:27Z blocker: none
Owner

[RECONCILER RESULT] Post-merge moot lease cleanup — PR #512 / issue #510

Identity / profile proof

  • Profile: prgs-reconciler
  • Identity: sysadmin
  • Org/repo: Scaled-Tech-Consulting/Gitea-Tools
  • Worktree: branches/reconcile-pr512-moot @ af938064cd7558872af888c41eef64380ff0a111

MCP / #515 behavior

  • Merged prgs/master includes gitea_cleanup_post_merge_moot_lease (#527).
  • Live reconciler MCP registry lacks tool descriptor until reconnect; executed via merged source.

Lease IDs inspected

ID Phase Profile Verdict
#6724 claimed prgs-reviewer stale/non-authoritative
#6761 released prgs-reconciler authoritative

#515 reconciliation

  • PR #512 merged/closed (c54ac0d4...); issue #510 closed.
  • Read-only: cleanup_allowed=false#6761 terminal.
  • apply=true: no mutation.

Cleanup mutation performed

None.

Skipped

  • Released marker POST: #6761 already terminal
  • Branch/worktree delete: dry-run not safe

Conclusion: Moot lease already resolved; #6724 superseded by #515 newest-terminal logic.

## [RECONCILER RESULT] Post-merge moot lease cleanup — PR #512 / issue #510 ### Identity / profile proof - **Profile:** `prgs-reconciler` - **Identity:** `sysadmin` - **Org/repo:** `Scaled-Tech-Consulting/Gitea-Tools` - **Worktree:** `branches/reconcile-pr512-moot` @ `af938064cd7558872af888c41eef64380ff0a111` ### MCP / #515 behavior - Merged `prgs/master` includes `gitea_cleanup_post_merge_moot_lease` (#527). - Live reconciler MCP registry lacks tool descriptor until reconnect; executed via merged source. ### Lease IDs inspected | ID | Phase | Profile | Verdict | |----|-------|---------|---------| | **#6724** | `claimed` | `prgs-reviewer` | stale/non-authoritative | | **#6761** | `released` | `prgs-reconciler` | **authoritative** | ### #515 reconciliation - PR #512 merged/closed (`c54ac0d4...`); issue #510 closed. - Read-only: `cleanup_allowed=false` — #6761 terminal. - apply=true: **no mutation**. ### Cleanup mutation performed **None.** ### Skipped - Released marker POST: #6761 already terminal - Branch/worktree delete: dry-run not safe **Conclusion:** Moot lease already resolved; #6724 superseded by #515 newest-terminal logic.
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Scaled-Tech-Consulting/Gitea-Tools#512