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

Merged
sysadmin merged 3 commits from fix/issue-843-cross-role-allocation-handoff into master 2026-07-23 14:57:46 -05:00
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
Dismissed
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]>
jcwalker3 added 1 commit 2026-07-23 12:31:11 -05:00
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: released
candidate_head: a6c15afec1
target_branch: master
target_branch_sha: 4f3a464a90
last_activity: 2026-07-23T19:40:25Z
expires_at: 2026-07-23T19:50:25Z
blocker: obsolete-superseded-or-expired-lease

<!-- 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: released candidate_head: a6c15afec1ff3c154541bf65ecc196a317256c72 target_branch: master target_branch_sha: 4f3a464a90e14a6595cfb7339dd3d8d6ed957c93 last_activity: 2026-07-23T19:40:25Z expires_at: 2026-07-23T19:50:25Z blocker: obsolete-superseded-or-expired-lease
Owner

Canonical obsolete reviewer lease cleanup (#691)

  • pr: fix: make cross-role allocations consumable by independent workers (Closes #843) (#845)
  • lease_comment_id: 14778
  • session_id: 27430-647309de43e2
  • leased_head: a6c15afec1
  • current_head: f0c9ffb25e
  • expires_at: 2026-07-23T06:59:08Z
  • classification: foreign_expired_superseded_head
  • terminal_review_verdict: REQUEST_CHANGES
  • tool: gitea_cleanup_obsolete_reviewer_comment_lease
  • action: posted terminal phase=released lease marker; did not transfer validation, decision, or workflow proof; did not repoint lease to the new head; did not delete history
## Canonical obsolete reviewer lease cleanup (#691) - pr: #845 - lease_comment_id: 14778 - session_id: 27430-647309de43e2 - leased_head: a6c15afec1ff3c154541bf65ecc196a317256c72 - current_head: f0c9ffb25e7bf3d028e22f93915d5e98178a1043 - expires_at: 2026-07-23T06:59:08Z - classification: foreign_expired_superseded_head - terminal_review_verdict: REQUEST_CHANGES - tool: gitea_cleanup_obsolete_reviewer_comment_lease - action: posted terminal phase=released lease marker; did not transfer validation, decision, or workflow proof; did not repoint lease to the new head; did not delete history
Owner

repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #845
issue: #843
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 91859-aa76c1094515
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-845
phase: claimed
candidate_head: f0c9ffb25e
target_branch: master
target_branch_sha: 1c455b6ec0
last_activity: 2026-07-23T19:41:29Z
expires_at: 2026-07-23T19:51:29Z
blocker: none

<!-- mcp-review-lease:v1 --> repo: Scaled-Tech-Consulting/Gitea-Tools pr: #845 issue: #843 reviewer_identity: sysadmin profile: prgs-reviewer session_id: 91859-aa76c1094515 worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-845 phase: claimed candidate_head: f0c9ffb25e7bf3d028e22f93915d5e98178a1043 target_branch: master target_branch_sha: 1c455b6ec0f9cb761fe6248de68c17e061fb5ecd last_activity: 2026-07-23T19:41:29Z expires_at: 2026-07-23T19:51:29Z blocker: none
sysadmin approved these changes 2026-07-23 14:43:04 -05:00
sysadmin left a comment
Owner

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

Independent re-review by sysadmin / prgs-reviewer, distinct from author jcwalker3. Reviewed the live head f0c9ffb25e only.

Live head is a merge commit — parents 5eb89f8 (the review #515 F1 remediation) + 1c455b6 (current master) — "Merge branch 'master' into fix/issue-843…". git diff 5eb89f8..f0c9ffb2 over the five fix files is empty: the merge brought in master only and smuggled no edits into the fix. The security fix content is commit 5eb89f8, present and unchanged at the reviewed head. Diff-vs-master touches exactly 5 files (allocator_service.py, control_plane_db.py, gitea_mcp_server.py, lease_lifecycle.py, tests/test_issue_843_cross_role_handoff.py) — scope clean.

Prior blocking review #515 (REQUEST_CHANGES @ a6c15af) — STALE and F1 fully remediated

Review #515 applies only to the superseded head a6c15af (Gitea marks it stale). Its single blocking finding F1 — consume role/profile not authoritatively bound to the caller is fully remediated:

MCP boundary (gitea_mcp_server.py::gitea_adopt_workflow_lease):

  • The vulnerable role=(role or active_role).strip() or "author" is removed. The adopter role is now active_role = _profile_role_kind(profile) — derived authoritatively from the authenticated profile, never from caller input.
  • Underivable profile role fails closed (no silent "author" default).
  • A caller-supplied role that disagrees with the profile-derived role is rejected (outcome: blocked), no reinterpretation.
  • The boundary passes role=active_role plus adopter_profile_name and adopter_namespace (derived from the profile) downstream for provenance validation.

Library (lease_lifecycle.adopt_lease):

  • Cross-role handoff consume requires adopter_role == required_role (raise otherwise) — and adopter_role is now the authoritative profile-derived role.
  • Provenance required_profile / required_namespace are validated against the caller context; mismatch fails closed.
  • Terminal (abandoned/released) rejected; freshness limited to active / stale_dead_process / stale_missing_worktree; read-after-write adopted_by_session_id ownership evidence emitted.

Missing-test gap closed: new MCPBoundaryAdoptRoleBindingTest proves at the tool boundary that a reviewer profile and a merger profile cannot consume an author handoff via role="author" (test_reviewer_profile_cannot_consume_author_handoff_via_role_author, test_merger_…), that a reviewer profile is rejected even with no role argument, that an author profile supplying a mismatching role is rejected, that a foreign required_profile is rejected, and that the legitimate prgs-author profile still consumes. The original attack path from F1 (any gitea.read namespace hijacking an author allocation) is now closed and regression-tested.

#843 acceptance criteria — satisfied

Independent-author adopt; adopt after controller process exit; adopt without sharing controller session id; wrong-role rejected; second/concurrent adoption rejected without corruption; terminal adoption rejected; read-after-write ownership evidence; genuine-abandon recovery still valid; process_work_queue/allocate payload carries consume_allocation identifiers + required_role/required_profile/required_namespace instructions; same-role compatibility. Each is covered by a passing test.

Scope

Defect A only. No #844 epic/container queue exclusion implemented — verified. No unrelated files.

Independent validation (reviewer-run, live head f0c9ffb2, project venv)

pytest -s 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 in 1.74s

F1 security subset by name (10 passed): MCPBoundaryAdoptRoleBindingTest (7) + wrong_role / terminal / second-adoption rejection.

No new blocker found.

Canonical PR State

STATE: approved
WHO_IS_NEXT: merger
BLOCKED_ROLE: none
NEXT_ACTION: Merger merges PR #845 into master at approved head f0c9ffb25e7bf3d028e22f93915d5e98178a1043 (do not merge as reviewer; roles separate).
NEXT_PROMPT: As a fresh standalone gitea-merger session, merge PR #845 (Closes #843) into master at the approved head f0c9ffb25e7bf3d028e22f93915d5e98178a1043; verify approval is at current head and no new commits landed; then close #843 and clean up per the merge workflow.
WHAT_HAPPENED: Independent reviewer re-reviewed the live head after the author remediated blocking review #515; neutralized an obsolete crashed-session reviewer lease via sanctioned guarded cleanup, acquired a fresh reviewer lease at the current head, inspected the full remediation diff and surrounding implementation, ran the prescribed suite (100 passed) plus the F1 security subset (10 passed), and confirmed F1 fully remediated with the missing MCP-boundary test added.
WHY: F1's caller-supplied-role hole is closed at both the MCP boundary (authoritative profile-derived role, reject mismatch, fail closed on underivable role) and the library (role + required_profile/required_namespace provenance validation), and the previously-missing wrong-profile MCP-boundary regression now exists and passes; #843 ACs met; scope clean; no #844.
ISSUE: #843
HEAD_SHA: f0c9ffb25e7bf3d028e22f93915d5e98178a1043
REVIEW_STATUS: APPROVED
MERGE_READY: yes
BLOCKERS: none
VALIDATION: pytest (prescribed suite) → 100 passed; F1 security subset → 10 passed (reviewer-run at live head f0c9ffb2)
NATIVE_REVIEW_PROOF: sysadmin/prgs-reviewer submitted APPROVE at head f0c9ffb25e7bf3d028e22f93915d5e98178a1043 via native gitea_submit_pr_review (lease comment 14923, session 91859-aa76c1094515)
LAST_UPDATED_BY: prgs-reviewer (sysadmin)

[THREAD STATE LEDGER]
- current_state: approved
- blocker: none
- who_is_next: merger
- reviewed_head: f0c9ffb25e7bf3d028e22f93915d5e98178a1043
- validation: 100 passed (prescribed suite) + 10 passed (F1 security subset) at reviewed head
- blocking_findings: 0
- prior_review_515: stale @ a6c15af; F1 remediated at 5eb89f8 (ancestor of reviewed head)
- scope: cross-role allocation handoff only; Issue #844 not implemented (verified)

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

## Review verdict: APPROVE — PR #845 (Closes #843) Independent re-review by **sysadmin / prgs-reviewer**, distinct from author **jcwalker3**. Reviewed the **live head f0c9ffb25e7bf3d028e22f93915d5e98178a1043** only. Live head is a merge commit — parents `5eb89f8` (the review #515 F1 remediation) + `1c455b6` (current master) — "Merge branch 'master' into fix/issue-843…". `git diff 5eb89f8..f0c9ffb2` over the five fix files is empty: the merge brought in master only and smuggled no edits into the fix. The security fix content is commit `5eb89f8`, present and unchanged at the reviewed head. Diff-vs-master touches exactly 5 files (allocator_service.py, control_plane_db.py, gitea_mcp_server.py, lease_lifecycle.py, tests/test_issue_843_cross_role_handoff.py) — scope clean. ### Prior blocking review #515 (REQUEST_CHANGES @ a6c15af) — STALE and F1 fully remediated Review #515 applies only to the superseded head `a6c15af` (Gitea marks it `stale`). Its single blocking finding **F1 — consume role/profile not authoritatively bound to the caller** is fully remediated: **MCP boundary (`gitea_mcp_server.py::gitea_adopt_workflow_lease`):** - The vulnerable `role=(role or active_role).strip() or "author"` is **removed**. The adopter role is now `active_role = _profile_role_kind(profile)` — derived authoritatively from the authenticated profile, never from caller input. - Underivable profile role **fails closed** (no silent `"author"` default). - A caller-supplied `role` that disagrees with the profile-derived role is **rejected** (`outcome: blocked`), no reinterpretation. - The boundary passes `role=active_role` plus `adopter_profile_name` and `adopter_namespace` (derived from the profile) downstream for provenance validation. **Library (`lease_lifecycle.adopt_lease`):** - Cross-role handoff consume requires `adopter_role == required_role` (raise otherwise) — and `adopter_role` is now the authoritative profile-derived role. - Provenance `required_profile` / `required_namespace` are validated against the caller context; mismatch fails closed. - Terminal (abandoned/released) rejected; freshness limited to active / stale_dead_process / stale_missing_worktree; read-after-write `adopted_by_session_id` ownership evidence emitted. **Missing-test gap closed:** new `MCPBoundaryAdoptRoleBindingTest` proves at the tool boundary that a reviewer profile and a merger profile cannot consume an author handoff via `role="author"` (`test_reviewer_profile_cannot_consume_author_handoff_via_role_author`, `test_merger_…`), that a reviewer profile is rejected even with **no** `role` argument, that an author profile supplying a mismatching role is rejected, that a foreign `required_profile` is rejected, and that the legitimate `prgs-author` profile still consumes. The original attack path from F1 (any `gitea.read` namespace hijacking an author allocation) is now closed and regression-tested. ### #843 acceptance criteria — satisfied Independent-author adopt; adopt after controller process exit; adopt without sharing controller session id; wrong-role rejected; second/concurrent adoption rejected without corruption; terminal adoption rejected; read-after-write ownership evidence; genuine-abandon recovery still valid; `process_work_queue`/allocate payload carries `consume_allocation` identifiers + `required_role`/`required_profile`/`required_namespace` instructions; same-role compatibility. Each is covered by a passing test. ### Scope Defect A only. No #844 epic/container queue exclusion implemented — verified. No unrelated files. ### Independent validation (reviewer-run, live head f0c9ffb2, project venv) ```text pytest -s 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 in 1.74s ``` F1 security subset by name (10 passed): MCPBoundaryAdoptRoleBindingTest (7) + wrong_role / terminal / second-adoption rejection. No new blocker found. ## Canonical PR State ```text STATE: approved WHO_IS_NEXT: merger BLOCKED_ROLE: none NEXT_ACTION: Merger merges PR #845 into master at approved head f0c9ffb25e7bf3d028e22f93915d5e98178a1043 (do not merge as reviewer; roles separate). NEXT_PROMPT: As a fresh standalone gitea-merger session, merge PR #845 (Closes #843) into master at the approved head f0c9ffb25e7bf3d028e22f93915d5e98178a1043; verify approval is at current head and no new commits landed; then close #843 and clean up per the merge workflow. WHAT_HAPPENED: Independent reviewer re-reviewed the live head after the author remediated blocking review #515; neutralized an obsolete crashed-session reviewer lease via sanctioned guarded cleanup, acquired a fresh reviewer lease at the current head, inspected the full remediation diff and surrounding implementation, ran the prescribed suite (100 passed) plus the F1 security subset (10 passed), and confirmed F1 fully remediated with the missing MCP-boundary test added. WHY: F1's caller-supplied-role hole is closed at both the MCP boundary (authoritative profile-derived role, reject mismatch, fail closed on underivable role) and the library (role + required_profile/required_namespace provenance validation), and the previously-missing wrong-profile MCP-boundary regression now exists and passes; #843 ACs met; scope clean; no #844. ISSUE: #843 HEAD_SHA: f0c9ffb25e7bf3d028e22f93915d5e98178a1043 REVIEW_STATUS: APPROVED MERGE_READY: yes BLOCKERS: none VALIDATION: pytest (prescribed suite) → 100 passed; F1 security subset → 10 passed (reviewer-run at live head f0c9ffb2) NATIVE_REVIEW_PROOF: sysadmin/prgs-reviewer submitted APPROVE at head f0c9ffb25e7bf3d028e22f93915d5e98178a1043 via native gitea_submit_pr_review (lease comment 14923, session 91859-aa76c1094515) LAST_UPDATED_BY: prgs-reviewer (sysadmin) [THREAD STATE LEDGER] - current_state: approved - blocker: none - who_is_next: merger - reviewed_head: f0c9ffb25e7bf3d028e22f93915d5e98178a1043 - validation: 100 passed (prescribed suite) + 10 passed (F1 security subset) at reviewed head - blocking_findings: 0 - prior_review_515: stale @ a6c15af; F1 remediated at 5eb89f8 (ancestor of reviewed head) - scope: cross-role allocation handoff only; Issue #844 not implemented (verified) ``` 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: 91859-aa76c1094515
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-845
phase: released
candidate_head: f0c9ffb25e
target_branch: master
target_branch_sha: 1c455b6ec0
last_activity: 2026-07-23T19:43:38Z
expires_at: 2026-07-23T19:53:38Z
blocker: manual-release

<!-- mcp-review-lease:v1 --> repo: Scaled-Tech-Consulting/Gitea-Tools pr: #845 issue: #843 reviewer_identity: sysadmin profile: prgs-reviewer session_id: 91859-aa76c1094515 worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-845 phase: released candidate_head: f0c9ffb25e7bf3d028e22f93915d5e98178a1043 target_branch: master target_branch_sha: 1c455b6ec0f9cb761fe6248de68c17e061fb5ecd last_activity: 2026-07-23T19:43:38Z expires_at: 2026-07-23T19:53:38Z blocker: manual-release
Owner

repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #845
issue: #843
reviewer_identity: sysadmin
profile: prgs-merger
session_id: 91866-618c8059505c
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/merge-pr-845
phase: claimed
candidate_head: f0c9ffb25e
target_branch: master
target_branch_sha: 1c455b6ec0
last_activity: 2026-07-23T19:56:40Z
expires_at: 2026-07-23T20:06:40Z
blocker: none

<!-- mcp-review-lease:v1 --> repo: Scaled-Tech-Consulting/Gitea-Tools pr: #845 issue: #843 reviewer_identity: sysadmin profile: prgs-merger session_id: 91866-618c8059505c worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/merge-pr-845 phase: claimed candidate_head: f0c9ffb25e7bf3d028e22f93915d5e98178a1043 target_branch: master target_branch_sha: 1c455b6ec0f9cb761fe6248de68c17e061fb5ecd last_activity: 2026-07-23T19:56:40Z expires_at: 2026-07-23T20:06:40Z blocker: none
sysadmin merged commit b70d5f3efa into master 2026-07-23 14:57:46 -05:00
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