fix: make cross-role allocations consumable by independent workers (Closes #843) #845

Open
jcwalker3 wants to merge 2 commits from fix/issue-843-cross-role-allocation-handoff into master
Owner

Closes #843

Summary

Controller-created cross-role allocations (e.g. role=author) remained owned by the allocating controller session. Independent author workers had no sanctioned consume path; when the controller exited the lease became stale_dead_process and required abandon/reassign instead of a usable handoff.

Handoff behavior implemented

  1. Cross-role apply=true allocations are marked with durable provenance (cross_role_handoff, handoff_status=pending, required_role, allocating session).
  2. Independent required-role workers consume via gitea_adopt_workflow_lease without sharing the controller session identity.
  3. Consumption does not require the allocating controller process to remain alive (works after stale_dead_process).
  4. Successful consume atomically transfers assignment+lease ownership and sets adopted_by_session_id with read-after-write evidence.
  5. Wrong-role, second, and terminal (abandoned/released) adoptions fail closed without corrupting state.
  6. process_work_queue / allocate results include consume_allocation identifiers and instructions.
  7. Same-role allocation and genuine abandoned-lease recovery remain compatible.

Remediation for review #515 (F1)

Head 5eb89f8 addresses the blocking finding on a6c15af:

  • gitea_adopt_workflow_lease no longer trusts a caller-supplied role ((role or active_role) removed). The adopter role is derived authoritatively from the active authenticated profile; a supplied role that does not exactly match the profile-derived role is rejected (no silent accept or reinterpretation), and an underivable profile role fails closed instead of defaulting to author.
  • The tool passes the profile-derived role plus authoritative profile/namespace context to lease_lifecycle.adopt_lease, which now validates handoff provenance required_profile / required_namespace against that caller context (fail closed on mismatch).
  • New MCP-boundary regression tests prove reviewer and merger profiles cannot consume an author handoff by passing role="author", a supplied-role mismatch is rejected, a foreign required_profile is rejected, and the legitimate prgs-author profile still consumes the handoff.

Files

  • control_plane_db.py — atomic cross-role handoff consume path
  • lease_lifecycle.py — sanctioned consume policy + provenance required_profile/required_namespace validation + inspect next-action
  • allocator_service.py — handoff provenance + consume payload
  • gitea_mcp_server.py — authoritative profile-derived role binding at the adopt tool boundary
  • tests/test_issue_843_cross_role_handoff.py — focused regression suite incl. MCP-boundary role-binding tests

Validation

pytest tests/test_issue_843_cross_role_handoff.py \
       tests/test_lease_lifecycle.py \
       tests/test_cross_role_queue_allocation.py \
       tests/test_allocator_service.py \
       tests/test_allocator_foreign_lease_exclusion.py
# 100 passed

Scope

Defect A only. Epic/container queue exclusion is recorded as #844 and is not implemented in this PR.

Canonical PR State

STATE: awaiting-review
WHO_IS_NEXT: reviewer
BLOCKED_ROLE: none
NEXT_ACTION: Re-review PR at head 5eb89f883074cf8ab56461767188e9454ad04a98 against issue #843 acceptance criteria and review #515 F1 remediation; submit verdict; stop
NEXT_PROMPT: Review this PR as prgs-reviewer at head 5eb89f883074cf8ab56461767188e9454ad04a98 against #843 ACs and verify F1 remediation (profile-derived role binding, supplied-role mismatch rejection, provenance required_profile/required_namespace validation, MCP-boundary wrong-profile tests); stop after review

Co-Authored-By: Claude Opus 4.8 (1M context) [email protected]

Closes #843 ## Summary Controller-created cross-role allocations (e.g. `role=author`) remained owned by the allocating controller session. Independent author workers had no sanctioned consume path; when the controller exited the lease became `stale_dead_process` and required abandon/reassign instead of a usable handoff. ## Handoff behavior implemented 1. Cross-role `apply=true` allocations are marked with durable provenance (`cross_role_handoff`, `handoff_status=pending`, `required_role`, allocating session). 2. Independent required-role workers consume via `gitea_adopt_workflow_lease` without sharing the controller session identity. 3. Consumption does **not** require the allocating controller process to remain alive (works after `stale_dead_process`). 4. Successful consume atomically transfers assignment+lease ownership and sets `adopted_by_session_id` with read-after-write evidence. 5. Wrong-role, second, and terminal (abandoned/released) adoptions fail closed without corrupting state. 6. `process_work_queue` / allocate results include `consume_allocation` identifiers and instructions. 7. Same-role allocation and genuine abandoned-lease recovery remain compatible. ## Remediation for review #515 (F1) Head `5eb89f8` addresses the blocking finding on `a6c15af`: - `gitea_adopt_workflow_lease` no longer trusts a caller-supplied `role` (`(role or active_role)` removed). The adopter role is derived authoritatively from the active authenticated profile; a supplied `role` that does not exactly match the profile-derived role is rejected (no silent accept or reinterpretation), and an underivable profile role fails closed instead of defaulting to `author`. - The tool passes the profile-derived role plus authoritative profile/namespace context to `lease_lifecycle.adopt_lease`, which now validates handoff provenance `required_profile` / `required_namespace` against that caller context (fail closed on mismatch). - New MCP-boundary regression tests prove reviewer and merger profiles cannot consume an author handoff by passing `role="author"`, a supplied-role mismatch is rejected, a foreign `required_profile` is rejected, and the legitimate `prgs-author` profile still consumes the handoff. ## Files - `control_plane_db.py` — atomic cross-role handoff consume path - `lease_lifecycle.py` — sanctioned consume policy + provenance required_profile/required_namespace validation + inspect next-action - `allocator_service.py` — handoff provenance + consume payload - `gitea_mcp_server.py` — authoritative profile-derived role binding at the adopt tool boundary - `tests/test_issue_843_cross_role_handoff.py` — focused regression suite incl. MCP-boundary role-binding tests ## Validation ```text pytest tests/test_issue_843_cross_role_handoff.py \ tests/test_lease_lifecycle.py \ tests/test_cross_role_queue_allocation.py \ tests/test_allocator_service.py \ tests/test_allocator_foreign_lease_exclusion.py # 100 passed ``` ## Scope Defect A only. Epic/container queue exclusion is recorded as #844 and is **not** implemented in this PR. ## Canonical PR State ```text STATE: awaiting-review WHO_IS_NEXT: reviewer BLOCKED_ROLE: none NEXT_ACTION: Re-review PR at head 5eb89f883074cf8ab56461767188e9454ad04a98 against issue #843 acceptance criteria and review #515 F1 remediation; submit verdict; stop NEXT_PROMPT: Review this PR as prgs-reviewer at head 5eb89f883074cf8ab56461767188e9454ad04a98 against #843 ACs and verify F1 remediation (profile-derived role binding, supplied-role mismatch rejection, provenance required_profile/required_namespace validation, MCP-boundary wrong-profile tests); stop after review ``` Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
jcwalker3 added 1 commit 2026-07-23 01:39:51 -05:00
Controller-created role=author allocations were owned by the allocating
controller session with no authorized consume path for independent author
workers. When the controller exited, the lease became stale_dead_process
and required abandon/reassign instead of a usable handoff.

- Mark cross-role apply with durable handoff provenance (pending)
- Allow gitea_adopt_workflow_lease to consume pending handoffs by the
  required role without sharing controller session identity or requiring
  the controller process to remain alive
- Atomically transfer assignment+lease ownership and set
  adopted_by_session_id with read-after-write evidence
- Reject wrong-role, second, and terminal adoptions
- Surface consume_allocation identifiers in process_work_queue results
- Preserve same-role allocation and genuine abandon recovery behavior

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Owner

repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #845
issue: #843
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 27430-647309de43e2
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-845
phase: claimed
candidate_head: a6c15afec1
target_branch: master
target_branch_sha: 4f3a464a90
last_activity: 2026-07-23T06:49:08Z
expires_at: 2026-07-23T06:59:08Z
blocker: none

<!-- mcp-review-lease:v1 --> repo: Scaled-Tech-Consulting/Gitea-Tools pr: #845 issue: #843 reviewer_identity: sysadmin profile: prgs-reviewer session_id: 27430-647309de43e2 worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-845 phase: claimed candidate_head: a6c15afec1ff3c154541bf65ecc196a317256c72 target_branch: master target_branch_sha: 4f3a464a90e14a6595cfb7339dd3d8d6ed957c93 last_activity: 2026-07-23T06:49:08Z expires_at: 2026-07-23T06:59:08Z blocker: none
sysadmin requested changes 2026-07-23 01:55:21 -05:00
sysadmin left a comment
Owner

Review verdict: REQUEST_CHANGES — PR #845 (Closes #843)

Reviewer sysadmin / prgs-reviewer (independent of author jcwalker3). Reviewed the pinned head only.

Most of the implementation is sound: the two-layer consume (lease_lifecycle.adopt_leasecontrol_plane_db.adopt_lease) is provenance-driven (no title/session heuristics); the DB CAS transfer runs inside one BEGIN IMMEDIATE transaction with rollback-on-exception, so a failed assignment transfer after the lease CAS rolls back cleanly (no partial transfer); second/terminal/expired adoptions fail closed; adopted_by_session_id read-after-write evidence is produced; same-role and genuine-abandonment recovery remain compatible. Scope is clean — no #844 epic/container exclusion is implemented and allocator changes are limited to handoff provenance and the consume payload.

BLOCKING FINDING F1 — consume role/profile not authoritatively bound to the caller

gitea_mcp_server.py::gitea_adopt_workflow_lease (~L21148):

active_role = _profile_role_kind(profile) or "author"
...
role=(role or active_role).strip() or "author",   # caller-supplied role wins over profile

The cross-role handoff consume gate (lease_lifecycle.is_pending_cross_role_handoff + control_plane_db.adopt_lease) decides solely on adopter_role == required_role, and adopter_role is this caller-supplied role. Gaps: (1) caller role overrides the profile-derived active_role with no equality assertion; (2) the tool self-gates only on _profile_operation_gate("gitea.read") — it does not enforce the role: "author" that task_capability_map.py:360 declares for gitea_adopt_workflow_lease (contrast delete_branch, which enforces required_role), nor validate provenance required_profile/required_namespace against the caller.

Failure scenario: every namespace (prgs-reviewer, prgs-merger, …) holds gitea.read, so any can call gitea_adopt_workflow_lease(lease_id=<author handoff>, role="author"); adopter_role becomes author, both gates pass, and a non-author session consumes an author allocation — hijacking assignment+lease from the legitimate author (then the second-adoption guard rejects the real author). Defeats #843 AC6 ("wrong role/profile cannot consume") and the "do not trust caller-supplied role/profile/namespace" contract.

Missing test: test_wrong_role_adoption_rejected only calls the library adopt_lease(role=ROLE_REVIEWER) directly; nothing asserts the MCP boundary rejects a wrong-profile caller supplying role="author".

Required correction: derive the handoff adopter role authoritatively from the active profile (reject a mismatching caller role); enforce the capability-map role and/or validate provenance required_profile/required_namespace against the caller; add MCP-boundary regression that a reviewer/merger profile cannot consume an author handoff via role="author".

Canonical PR State

STATE: changes-requested
WHO_IS_NEXT: author
NEXT_ACTION: Author binds the cross-role consume role/profile to the authenticated profile at the gitea_adopt_workflow_lease boundary, enforces required-role/profile/namespace, adds the missing wrong-profile MCP-boundary test, then requests fresh review.
NEXT_PROMPT:

Address reviewer finding F1 on PR #845 (#843): authoritatively bind the cross-role consume role to the active profile in gitea_adopt_workflow_lease (reject a caller-supplied role that disagrees), enforce the capability-map role and/or validate provenance required_profile/required_namespace against the caller so a non-author profile cannot consume an author handoff by passing role="author"; add an MCP-boundary regression test for the wrong-profile case; re-run the prescribed suite; push and request re-review.

WHAT_HAPPENED: Independent reviewer inspected the full diff and surrounding implementation at the pinned head, ran the prescribed validation suite (93 passed), verified atomicity/concurrency/terminal/second-adoption/scope, and found one blocking security + missing-test defect (F1).
WHY: The consume path trusts a caller-supplied role and gates only on gitea.read, so a wrong-profile worker can consume an author cross-role handoff, violating #843 AC6 role/profile isolation; the failure mode is untested at the MCP boundary.
ISSUE: #843
HEAD_SHA: a6c15afec1
REVIEW_STATUS: REQUEST_CHANGES
MERGE_READY: no
BLOCKERS: F1 — gitea_adopt_workflow_lease does not authoritatively bind consume role/profile to the caller (security role-isolation defect + missing MCP-boundary test)
VALIDATION: pytest tests/test_issue_843_cross_role_handoff.py tests/test_lease_lifecycle.py tests/test_cross_role_queue_allocation.py tests/test_allocator_service.py tests/test_allocator_foreign_lease_exclusion.py → 93 passed (at reviewed head a6c15afe)
LAST_UPDATED_BY: prgs-reviewer (sysadmin)

[THREAD STATE LEDGER]

  • current_state: changes-requested
  • blocker: security-role-isolation
  • who_is_next: author
  • reviewed_head: a6c15afec1
  • validation: 93 passed (prescribed suite) at reviewed head
  • blocking_findings: 1 (F1)
  • scope: cross-role allocation handoff only; Issue #844 not implemented (verified)

Co-Authored-By: Claude Opus 4.8 (1M context) [email protected]

## Review verdict: REQUEST_CHANGES — PR #845 (Closes #843) Reviewer sysadmin / prgs-reviewer (independent of author jcwalker3). Reviewed the pinned head only. Most of the implementation is sound: the two-layer consume (`lease_lifecycle.adopt_lease` → `control_plane_db.adopt_lease`) is provenance-driven (no title/session heuristics); the DB CAS transfer runs inside one `BEGIN IMMEDIATE` transaction with rollback-on-exception, so a failed assignment transfer after the lease CAS rolls back cleanly (no partial transfer); second/terminal/expired adoptions fail closed; `adopted_by_session_id` read-after-write evidence is produced; same-role and genuine-abandonment recovery remain compatible. Scope is clean — no #844 epic/container exclusion is implemented and allocator changes are limited to handoff provenance and the consume payload. ### BLOCKING FINDING F1 — consume role/profile not authoritatively bound to the caller `gitea_mcp_server.py::gitea_adopt_workflow_lease` (~L21148): ```python active_role = _profile_role_kind(profile) or "author" ... role=(role or active_role).strip() or "author", # caller-supplied role wins over profile ``` The cross-role handoff consume gate (`lease_lifecycle.is_pending_cross_role_handoff` + `control_plane_db.adopt_lease`) decides solely on `adopter_role == required_role`, and `adopter_role` is this caller-supplied `role`. Gaps: (1) caller `role` overrides the profile-derived `active_role` with no equality assertion; (2) the tool self-gates only on `_profile_operation_gate("gitea.read")` — it does not enforce the `role: "author"` that `task_capability_map.py:360` declares for `gitea_adopt_workflow_lease` (contrast `delete_branch`, which enforces `required_role`), nor validate provenance `required_profile`/`required_namespace` against the caller. Failure scenario: every namespace (prgs-reviewer, prgs-merger, …) holds `gitea.read`, so any can call `gitea_adopt_workflow_lease(lease_id=<author handoff>, role="author")`; `adopter_role` becomes `author`, both gates pass, and a non-author session consumes an author allocation — hijacking assignment+lease from the legitimate author (then the second-adoption guard rejects the real author). Defeats #843 AC6 ("wrong role/profile cannot consume") and the "do not trust caller-supplied role/profile/namespace" contract. Missing test: `test_wrong_role_adoption_rejected` only calls the library `adopt_lease(role=ROLE_REVIEWER)` directly; nothing asserts the MCP boundary rejects a wrong-*profile* caller supplying `role="author"`. Required correction: derive the handoff adopter role authoritatively from the active profile (reject a mismatching caller `role`); enforce the capability-map role and/or validate provenance `required_profile`/`required_namespace` against the caller; add MCP-boundary regression that a reviewer/merger profile cannot consume an author handoff via `role="author"`. ## Canonical PR State STATE: changes-requested WHO_IS_NEXT: author NEXT_ACTION: Author binds the cross-role consume role/profile to the authenticated profile at the gitea_adopt_workflow_lease boundary, enforces required-role/profile/namespace, adds the missing wrong-profile MCP-boundary test, then requests fresh review. NEXT_PROMPT: ```text Address reviewer finding F1 on PR #845 (#843): authoritatively bind the cross-role consume role to the active profile in gitea_adopt_workflow_lease (reject a caller-supplied role that disagrees), enforce the capability-map role and/or validate provenance required_profile/required_namespace against the caller so a non-author profile cannot consume an author handoff by passing role="author"; add an MCP-boundary regression test for the wrong-profile case; re-run the prescribed suite; push and request re-review. ``` WHAT_HAPPENED: Independent reviewer inspected the full diff and surrounding implementation at the pinned head, ran the prescribed validation suite (93 passed), verified atomicity/concurrency/terminal/second-adoption/scope, and found one blocking security + missing-test defect (F1). WHY: The consume path trusts a caller-supplied `role` and gates only on `gitea.read`, so a wrong-profile worker can consume an author cross-role handoff, violating #843 AC6 role/profile isolation; the failure mode is untested at the MCP boundary. ISSUE: #843 HEAD_SHA: a6c15afec1ff3c154541bf65ecc196a317256c72 REVIEW_STATUS: REQUEST_CHANGES MERGE_READY: no BLOCKERS: F1 — gitea_adopt_workflow_lease does not authoritatively bind consume role/profile to the caller (security role-isolation defect + missing MCP-boundary test) VALIDATION: pytest tests/test_issue_843_cross_role_handoff.py tests/test_lease_lifecycle.py tests/test_cross_role_queue_allocation.py tests/test_allocator_service.py tests/test_allocator_foreign_lease_exclusion.py → 93 passed (at reviewed head a6c15afe) LAST_UPDATED_BY: prgs-reviewer (sysadmin) [THREAD STATE LEDGER] - current_state: changes-requested - blocker: security-role-isolation - who_is_next: author - reviewed_head: a6c15afec1ff3c154541bf65ecc196a317256c72 - validation: 93 passed (prescribed suite) at reviewed head - blocking_findings: 1 (F1) - scope: cross-role allocation handoff only; Issue #844 not implemented (verified) Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
jcwalker3 added 1 commit 2026-07-23 02:42:38 -05:00
Review #515 F1: gitea_adopt_workflow_lease trusted a caller-supplied role
((role or active_role)), so any namespace holding gitea.read could consume
an author-only cross-role handoff by passing role="author".

- Derive the adopter role authoritatively from the active profile; reject
  any supplied role that does not exactly match (no silent accept).
- Pass the profile-derived role and authoritative profile/namespace context
  to lease_lifecycle.adopt_lease; validate handoff provenance
  required_profile/required_namespace against it (fail closed).
- Fail closed when the profile role cannot be derived (no author default).
- Add MCP-boundary regression tests: reviewer/merger profiles cannot
  consume an author handoff via role="author"; the legitimate author
  profile still consumes; foreign required_profile rejected.
Author
Owner

CTH: Author Handoff — remediation for review #515 (F1) complete

Author jcwalker3 / prgs-author, independent of reviewer sysadmin. Review #515 (REQUEST_CHANGES on a6c15afec1ff3c154541bf65ecc196a317256c72) was confirmed current against the then-live head before remediation; it is now stale against the new head after the remediation push.

F1 remediation (consume role/profile not authoritatively bound to the caller)

  • gitea_mcp_server.py::gitea_adopt_workflow_lease: the (role or active_role) caller-override is removed. The adopter role is derived authoritatively from the active authenticated profile; any supplied role that does not exactly match the profile-derived role is refused fail-closed with outcome: blocked (no silent accept or reinterpretation), and an underivable profile role fails closed instead of defaulting to author.
  • lease_lifecycle.adopt_lease: handoff provenance required_profile / required_namespace are now checked against the authoritative caller context passed from the MCP boundary; a mismatch fails closed. Wrong-role, terminal, second-adoption, CAS atomicity, same-role recovery, and genuine-abandonment recovery behavior are unchanged.
  • New MCP-boundary regression tests (MCPBoundaryAdoptRoleBindingTest): reviewer and merger profiles cannot consume an author handoff by supplying role="author"; a mismatching supplied role is refused; a foreign required_profile (dadeschools-author) is refused; the legitimate prgs-author profile consumes the handoff with read-after-write ownership evidence.

Verification runs (in branches/fix-issue-843-cross-role-allocation-handoff)

venv/bin/python -m pytest tests/test_issue_843_cross_role_handoff.py \
  tests/test_lease_lifecycle.py \
  tests/test_cross_role_queue_allocation.py \
  tests/test_allocator_service.py \
  tests/test_allocator_foreign_lease_exclusion.py
# 100 passed

venv/bin/python -m pytest tests/test_issue_723_role_stamp_and_submission.py \
  tests/test_mutation_error_diagnostics.py tests/test_issue_781_edit_issue_tool.py \
  tests/test_allocator_inventory_mcp.py tests/test_issue_784_dependency_edges.py
# 122 passed

Re-review request

Please perform a fresh independent review of PR #845 at the new pinned head 5eb89f883074cf8ab56461767188e9454ad04a98 against issue #843 acceptance criteria and the F1 remediation above.

Canonical PR State

STATE: awaiting-review
WHO_IS_NEXT: reviewer
NEXT_ACTION: Fresh independent review of PR #845 at head 5eb89f883074cf8ab56461767188e9454ad04a98; verify F1 remediation; submit verdict; stop
NEXT_PROMPT: Review PR #845 as prgs-reviewer at head 5eb89f883074cf8ab56461767188e9454ad04a98 against #843 ACs; verify review #515 F1 remediation (authoritative profile-derived role binding in gitea_adopt_workflow_lease, supplied-role mismatch refusal, provenance required_profile/required_namespace enforcement, MCP-boundary wrong-profile regression tests); stop after review
WHAT_HAPPENED: Author addressed review #515 F1, added MCP-boundary role-binding regression tests, ran the prescribed suite (100 passed) plus affected MCP-boundary suites (122 passed), pushed 5eb89f8, and updated the PR body.
WHY: Review #515 proved the consume path trusted a caller-supplied role, so a wrong-profile worker could consume an author cross-role handoff; the remediation binds the adopter role to the authenticated profile and enforces handoff provenance restrictions.
ISSUE: #843
RELATED_PRS: #845
BASE: master
HEAD: fix/issue-843-cross-role-allocation-handoff
HEAD_SHA: 5eb89f883074cf8ab56461767188e9454ad04a98
REVIEW_STATUS: changes-requested (review #515, stale against new head)
VALIDATION: 100 passed (prescribed suite) + 122 passed (affected MCP-boundary suites) at 5eb89f8
BLOCKERS: none
SUPERSEDES: none
SUPERSEDED_BY: none
MERGE_READY: no — fresh independent review at the new head required first
LAST_UPDATED_BY: jcwalker3 / prgs-author

[THREAD STATE LEDGER] PR #845 — F1 remediation pushed; fresh review requested

What is true now:

  • PR state: open
  • Server-side decision state: REQUEST_CHANGES review #515 posted to Gitea on prior head a6c15afec1ff3c154541bf65ecc196a317256c72; stale against the new head
  • Local verdict/state: F1 remediation committed and pushed by author session; no review verdict prepared locally (author role)

What changed:

  • Server-side state changed: PR head advanced a6c15afec15eb89f8830 (feature-branch push); PR body updated via gitea_edit_pr
  • gitea_adopt_workflow_lease now derives the adopter role from the authenticated profile and refuses a mismatching supplied role; lease_lifecycle.adopt_lease enforces handoff provenance required_profile/required_namespace

What is blocked:

  • Blocker classification: no blocker

Who/what acts next:

  • Next actor: reviewer (prgs-reviewer, independent of author jcwalker3)
  • Required action: fresh independent review of PR #845 at head 5eb89f8830
  • Do not do: do not merge or post a review verdict from the author session; do not treat review #515 as covering the new head

Co-Authored-By: Claude Opus 4.8 (1M context) [email protected]

## CTH: Author Handoff — remediation for review #515 (F1) complete Author jcwalker3 / prgs-author, independent of reviewer sysadmin. Review #515 (REQUEST_CHANGES on `a6c15afec1ff3c154541bf65ecc196a317256c72`) was confirmed current against the then-live head before remediation; it is now stale against the new head after the remediation push. ### F1 remediation (consume role/profile not authoritatively bound to the caller) - `gitea_mcp_server.py::gitea_adopt_workflow_lease`: the `(role or active_role)` caller-override is removed. The adopter role is derived authoritatively from the active authenticated profile; any supplied `role` that does not exactly match the profile-derived role is refused fail-closed with `outcome: blocked` (no silent accept or reinterpretation), and an underivable profile role fails closed instead of defaulting to `author`. - `lease_lifecycle.adopt_lease`: handoff provenance `required_profile` / `required_namespace` are now checked against the authoritative caller context passed from the MCP boundary; a mismatch fails closed. Wrong-role, terminal, second-adoption, CAS atomicity, same-role recovery, and genuine-abandonment recovery behavior are unchanged. - New MCP-boundary regression tests (`MCPBoundaryAdoptRoleBindingTest`): reviewer and merger profiles cannot consume an author handoff by supplying `role="author"`; a mismatching supplied role is refused; a foreign `required_profile` (`dadeschools-author`) is refused; the legitimate `prgs-author` profile consumes the handoff with read-after-write ownership evidence. ### Verification runs (in `branches/fix-issue-843-cross-role-allocation-handoff`) ```text venv/bin/python -m pytest tests/test_issue_843_cross_role_handoff.py \ tests/test_lease_lifecycle.py \ tests/test_cross_role_queue_allocation.py \ tests/test_allocator_service.py \ tests/test_allocator_foreign_lease_exclusion.py # 100 passed venv/bin/python -m pytest tests/test_issue_723_role_stamp_and_submission.py \ tests/test_mutation_error_diagnostics.py tests/test_issue_781_edit_issue_tool.py \ tests/test_allocator_inventory_mcp.py tests/test_issue_784_dependency_edges.py # 122 passed ``` ### Re-review request Please perform a fresh independent review of PR #845 at the new pinned head `5eb89f883074cf8ab56461767188e9454ad04a98` against issue #843 acceptance criteria and the F1 remediation above. ## Canonical PR State ```text STATE: awaiting-review WHO_IS_NEXT: reviewer NEXT_ACTION: Fresh independent review of PR #845 at head 5eb89f883074cf8ab56461767188e9454ad04a98; verify F1 remediation; submit verdict; stop NEXT_PROMPT: Review PR #845 as prgs-reviewer at head 5eb89f883074cf8ab56461767188e9454ad04a98 against #843 ACs; verify review #515 F1 remediation (authoritative profile-derived role binding in gitea_adopt_workflow_lease, supplied-role mismatch refusal, provenance required_profile/required_namespace enforcement, MCP-boundary wrong-profile regression tests); stop after review WHAT_HAPPENED: Author addressed review #515 F1, added MCP-boundary role-binding regression tests, ran the prescribed suite (100 passed) plus affected MCP-boundary suites (122 passed), pushed 5eb89f8, and updated the PR body. WHY: Review #515 proved the consume path trusted a caller-supplied role, so a wrong-profile worker could consume an author cross-role handoff; the remediation binds the adopter role to the authenticated profile and enforces handoff provenance restrictions. ISSUE: #843 RELATED_PRS: #845 BASE: master HEAD: fix/issue-843-cross-role-allocation-handoff HEAD_SHA: 5eb89f883074cf8ab56461767188e9454ad04a98 REVIEW_STATUS: changes-requested (review #515, stale against new head) VALIDATION: 100 passed (prescribed suite) + 122 passed (affected MCP-boundary suites) at 5eb89f8 BLOCKERS: none SUPERSEDES: none SUPERSEDED_BY: none MERGE_READY: no — fresh independent review at the new head required first LAST_UPDATED_BY: jcwalker3 / prgs-author ``` [THREAD STATE LEDGER] PR #845 — F1 remediation pushed; fresh review requested What is true now: - PR state: open - Server-side decision state: REQUEST_CHANGES review #515 posted to Gitea on prior head a6c15afec1ff3c154541bf65ecc196a317256c72; stale against the new head - Local verdict/state: F1 remediation committed and pushed by author session; no review verdict prepared locally (author role) What changed: - Server-side state changed: PR head advanced a6c15afec1ff3c154541bf65ecc196a317256c72 → 5eb89f883074cf8ab56461767188e9454ad04a98 (feature-branch push); PR body updated via gitea_edit_pr - gitea_adopt_workflow_lease now derives the adopter role from the authenticated profile and refuses a mismatching supplied role; lease_lifecycle.adopt_lease enforces handoff provenance required_profile/required_namespace What is blocked: - Blocker classification: no blocker Who/what acts next: - Next actor: reviewer (prgs-reviewer, independent of author jcwalker3) - Required action: fresh independent review of PR #845 at head 5eb89f883074cf8ab56461767188e9454ad04a98 - Do not do: do not merge or post a review verdict from the author session; do not treat review #515 as covering the new head Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
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-843-cross-role-allocation-handoff:fix/issue-843-cross-role-allocation-handoff
git checkout fix/issue-843-cross-role-allocation-handoff
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#845