fix(mcp): invalidate review session state on cross-profile activation (Closes #690) #924

Open
jcwalker3 wants to merge 1 commits from fix/issue-690-review-profile-switch-guard into master
Owner

Summary

Hardens formal-review runs against cross-role profile activation mid-session (#690), spawned from the PR #688 / issue #687 formal-review recovery path.

A mid-run profile switch (e.g. reviewer → author → reviewer) previously left workflow-load proof, reviewer lease binding, the review decision lock, live namespace health, and preflight identity/capability stamps intact, so a formal verdict could be recorded under contaminated session state (wrong role stamp, stale lease, inherited capability).

Changes

  • gitea_activate_profile now treats a cross-profile switch as a session boundary and invalidates, in memory and in durable session state keyed by either profile identity:
    • preflight identity/capability stamps,
    • review workflow-load proof,
    • the review decision lock (incl. final_review_decision_ready),
    • the reviewer PR session lease,
    • live namespace-health assessments.
      The activation result reports review_state_invalidation + re_preflight_required with an exact next action.
  • Full reviewer preflight required after any switch before a formal verdict: gitea_whoami, gitea_load_review_workflow, gitea_resolve_task_capability(review_pr), live head re-pin, lease re-acquire/adopt. Switching back to the prior profile cannot resurrect the stale run (durable state cleared for both identities).
  • Namespace provenance (#690 AC4): optional launcher-declared GITEA_MCP_NAMESPACE is reported as namespace_provenance by gitea_whoami, gitea_get_runtime_context, and gitea_resolve_task_capability; a declared namespace that disagrees with a task's required namespace (review_prgitea-reviewer, merge_prgitea-merger) fails closed. Undeclared = unknown, never treated as proof.
  • Docs: supported pattern is separate session/namespace per role (dual-namespace), not in-process profile hopping mid-review; profile switching is the operator-approved exception with the re-preflight cost.

Acceptance criteria coverage

  1. Cross-role activation strictly isolated: review state invalidated unless fully re-preflighted.
  2. Workflow-load proof, lease binding, decision markers, capability state invalidated on profile change.
  3. Full reviewer preflight required post-switch (existing gates fail closed on cleared state).
  4. Namespace provenance reported; fail closed on task/namespace mismatch.
  5. Tests: mid-run switch without re-preflight blocks verdict gate; clean re-preflight allows; switch-back cannot resurrect; same-profile re-activation preserves state; namespace mismatch/unknown cases; whoami provenance; resolver fail-closed on mismatch.
  6. Docs updated (docs/gitea-execution-profiles.md, docs/mcp-namespace-health.md).

Test evidence

  • New: tests/test_issue_690_profile_switch_review_guard.py7/7 pass.
  • Targeted sweep (namespace/review-workflow/decision-lock/runtime/session/capability/preflight/profile, 728 tests): 724 pass; 4 failures reproduce identically on unmodified master (pre-existing baseline, verified by running the same tests on the master checkout).
  • Full suite comparison vs master baseline: see follow-up comment.

Non-goals respected

  • No #687 branch-delete/migration work; no process-kill recovery; #332 terminal-mutation accounting untouched.

Handoff

WHO_IS_NEXT: reviewer — please review against #690 ACs. Active-config proof: this PR was authored through the live gitea-author namespace (gitea_whoami verified jcwalker3 / prgs-author).

## Summary Hardens formal-review runs against cross-role profile activation mid-session (#690), spawned from the PR #688 / issue #687 formal-review recovery path. A mid-run profile switch (e.g. reviewer → author → reviewer) previously left workflow-load proof, reviewer lease binding, the review decision lock, live namespace health, and preflight identity/capability stamps intact, so a formal verdict could be recorded under contaminated session state (wrong role stamp, stale lease, inherited capability). ## Changes - **`gitea_activate_profile`** now treats a cross-profile switch as a session boundary and invalidates, in memory **and** in durable session state keyed by *either* profile identity: - preflight identity/capability stamps, - review workflow-load proof, - the review decision lock (incl. `final_review_decision_ready`), - the reviewer PR session lease, - live namespace-health assessments. The activation result reports `review_state_invalidation` + `re_preflight_required` with an exact next action. - **Full reviewer preflight required after any switch** before a formal verdict: `gitea_whoami`, `gitea_load_review_workflow`, `gitea_resolve_task_capability(review_pr)`, live head re-pin, lease re-acquire/adopt. Switching back to the prior profile cannot resurrect the stale run (durable state cleared for both identities). - **Namespace provenance (#690 AC4):** optional launcher-declared `GITEA_MCP_NAMESPACE` is reported as `namespace_provenance` by `gitea_whoami`, `gitea_get_runtime_context`, and `gitea_resolve_task_capability`; a declared namespace that disagrees with a task's required namespace (`review_pr` → `gitea-reviewer`, `merge_pr` → `gitea-merger`) fails closed. Undeclared = `unknown`, never treated as proof. - **Docs:** supported pattern is separate session/namespace per role (dual-namespace), not in-process profile hopping mid-review; profile switching is the operator-approved exception with the re-preflight cost. ## Acceptance criteria coverage 1. ✅ Cross-role activation strictly isolated: review state invalidated unless fully re-preflighted. 2. ✅ Workflow-load proof, lease binding, decision markers, capability state invalidated on profile change. 3. ✅ Full reviewer preflight required post-switch (existing gates fail closed on cleared state). 4. ✅ Namespace provenance reported; fail closed on task/namespace mismatch. 5. ✅ Tests: mid-run switch without re-preflight blocks verdict gate; clean re-preflight allows; switch-back cannot resurrect; same-profile re-activation preserves state; namespace mismatch/unknown cases; whoami provenance; resolver fail-closed on mismatch. 6. ✅ Docs updated (`docs/gitea-execution-profiles.md`, `docs/mcp-namespace-health.md`). ## Test evidence - New: `tests/test_issue_690_profile_switch_review_guard.py` — **7/7 pass**. - Targeted sweep (namespace/review-workflow/decision-lock/runtime/session/capability/preflight/profile, 728 tests): 724 pass; 4 failures reproduce **identically on unmodified master** (pre-existing baseline, verified by running the same tests on the master checkout). - Full suite comparison vs master baseline: see follow-up comment. ## Non-goals respected - No #687 branch-delete/migration work; no process-kill recovery; #332 terminal-mutation accounting untouched. ## Handoff **WHO_IS_NEXT:** reviewer — please review against #690 ACs. Active-config proof: this PR was authored through the live `gitea-author` namespace (`gitea_whoami` verified `jcwalker3` / `prgs-author`).
jcwalker3 added 1 commit 2026-07-25 18:26:04 -05:00
A mid-run profile switch (reviewer -> author -> reviewer) left workflow-load
proof, reviewer lease binding, the review decision lock, live namespace
health, and preflight identity/capability stamps intact, so a formal verdict
could be recorded under contaminated session state.

- gitea_activate_profile now invalidates all review-critical session state
  on a cross-profile switch, in memory and in durable state keyed by either
  profile identity, and reports the invalidation + re-preflight requirement.
- Full reviewer preflight (whoami, load_review_workflow,
  resolve_task_capability(review_pr), head re-pin, lease re-acquire) is
  required before any formal verdict after a switch; switching back cannot
  resurrect the stale run.
- Namespace provenance: optional launcher-declared GITEA_MCP_NAMESPACE is
  reported by whoami/runtime context/capability resolution, and a declared
  namespace that disagrees with a task's required namespace fails closed.
- Docs: supported pattern is separate session/namespace per role, not
  in-process profile hopping mid-review.
You are not authorized to merge this pull request.
This pull request can be merged automatically.
This branch is out-of-date with the base branch
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin fix/issue-690-review-profile-switch-guard:fix/issue-690-review-profile-switch-guard
git checkout fix/issue-690-review-profile-switch-guard
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

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