fix: make cross-role allocations consumable by independent workers (Closes #843) #845
Labels
Clear labels
allocator
anti-stomp
architecture
bug
chore
codex
concurrency
contamination
control-plane
dashboard
database
design
documentation
enhancement
gitea
glitchtip
important
incident
incident-bridge
integration
jenkins
labels
leases
mcp
mcp-health
mcp-menu
multi-project
mutating
nice-to-have
observability
portability
preflight
protected-branch
queue
read-only
reconnect
recovery
refactor
release
reliability
resumable-review
reviewer
roadmap
safety
security
self-hosted
sentry
stale-runtime
status:blocked
status:in-progress
status:pr-open
status:ready
terminal-lock
testing
tracker
type:bug
type:feature
type:feature
type:guardrail
visibility
workflow
workflow-hardening
workflow-hardening
bug
duplicate
enhancement
help wanted
invalid
question
wontfix
Controller-owned work allocator
Prevent concurrent LLM session stomping
Architecture / structural design
OpenAI Codex client / workflow session surface
Concurrent session safety
Workflow or session contamination incident
MCP control-plane coordination and allocation authority
MCP operational dashboard/queue view
Internal coordination storage (SQLite/Postgres)
Design / investigation, no implementation
Docs / runbooks
New feature or improvement
Gitea MCP workflow
GlitchTip integration
Operational or process incident requiring durable audit trail
Sentry-to-Gitea incident bridging
Integration testing
Jenkins integration
Label taxonomy management
Lease adopt/release/expire lifecycle
MCP server / tooling
MCP namespace and runtime health
MCP menu surface
Work spanning multiple monitoring projects or Gitea repos
Mutating action; requires gating
Observability, metrics, traces, error reporting
Cross-platform / portability
Shared preflight gates before mutation
Protected branch / stable-branch policy concern
Work queue visibility and allocation
Read-only, no mutation
MCP client reconnect/reload recovery path
Recovery paths for stale/foreign leases
Code refactor / restructure
Release / versioning
Reliability / failure handling
Persist and resume prepared review verdicts across sessions
Reviewer workflow tooling
Roadmap / umbrella issue
Safety rails and fail-closed mutation guards
Security / trust boundary
Self-hosted infrastructure integration
Sentry error monitoring integration
Stale backend daemon / runtime-vs-master parity failures
Issue is blocked
Issue is being worked on
Issue has an open pull request
Issue is ready for work
Terminal review lock (#332) path
Tests / test coverage
Issue tracker hygiene / meta
Bug or defect
Feature or enhancement
Feature or enhancement
Safety gate or guardrail
Workflow state visibility for LLMs/operators
Cross-tool workflow
LLM workflow coordination hardening
LLM workflow coordination hardening
Something is not working
This issue or pull request already exists
New feature
Need some help
Something is wrong
More information is needed
This won't be fixed
No labels
Milestone
No items
No Milestone
Projects
Clear projects
No projects
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: Scaled-Tech-Consulting/Gitea-Tools#845
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #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 becamestale_dead_processand required abandon/reassign instead of a usable handoff.Handoff behavior implemented
apply=trueallocations are marked with durable provenance (cross_role_handoff,handoff_status=pending,required_role, allocating session).gitea_adopt_workflow_leasewithout sharing the controller session identity.stale_dead_process).adopted_by_session_idwith read-after-write evidence.process_work_queue/ allocate results includeconsume_allocationidentifiers and instructions.Remediation for review #515 (F1)
Head
5eb89f8addresses the blocking finding ona6c15af:gitea_adopt_workflow_leaseno longer trusts a caller-suppliedrole((role or active_role)removed). The adopter role is derived authoritatively from the active authenticated profile; a suppliedrolethat 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 toauthor.lease_lifecycle.adopt_lease, which now validates handoff provenancerequired_profile/required_namespaceagainst that caller context (fail closed on mismatch).role="author", a supplied-role mismatch is rejected, a foreignrequired_profileis rejected, and the legitimateprgs-authorprofile still consumes the handoff.Files
control_plane_db.py— atomic cross-role handoff consume pathlease_lifecycle.py— sanctioned consume policy + provenance required_profile/required_namespace validation + inspect next-actionallocator_service.py— handoff provenance + consume payloadgitea_mcp_server.py— authoritative profile-derived role binding at the adopt tool boundarytests/test_issue_843_cross_role_handoff.py— focused regression suite incl. MCP-boundary role-binding testsValidation
Scope
Defect A only. Epic/container queue exclusion is recorded as #844 and is not implemented in this PR.
Canonical PR State
Co-Authored-By: Claude Opus 4.8 (1M context) [email protected]
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:
a6c15afec1target_branch: master
target_branch_sha:
4f3a464a90last_activity: 2026-07-23T06:49:08Z
expires_at: 2026-07-23T06:59:08Z
blocker: none
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 oneBEGIN IMMEDIATEtransaction 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_idread-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):The cross-role handoff consume gate (
lease_lifecycle.is_pending_cross_role_handoff+control_plane_db.adopt_lease) decides solely onadopter_role == required_role, andadopter_roleis this caller-suppliedrole. Gaps: (1) callerroleoverrides the profile-derivedactive_rolewith no equality assertion; (2) the tool self-gates only on_profile_operation_gate("gitea.read")— it does not enforce therole: "author"thattask_capability_map.py:360declares forgitea_adopt_workflow_lease(contrastdelete_branch, which enforcesrequired_role), nor validate provenancerequired_profile/required_namespaceagainst the caller.Failure scenario: every namespace (prgs-reviewer, prgs-merger, …) holds
gitea.read, so any can callgitea_adopt_workflow_lease(lease_id=<author handoff>, role="author");adopter_rolebecomesauthor, 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_rejectedonly calls the libraryadopt_lease(role=ROLE_REVIEWER)directly; nothing asserts the MCP boundary rejects a wrong-profile caller supplyingrole="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 provenancerequired_profile/required_namespaceagainst the caller; add MCP-boundary regression that a reviewer/merger profile cannot consume an author handoff viarole="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:
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
roleand gates only ongitea.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:
a6c15afec1REVIEW_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]
a6c15afec1Co-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 suppliedrolethat does not exactly match the profile-derived role is refused fail-closed withoutcome: blocked(no silent accept or reinterpretation), and an underivable profile role fails closed instead of defaulting toauthor.lease_lifecycle.adopt_lease: handoff provenancerequired_profile/required_namespaceare 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.MCPBoundaryAdoptRoleBindingTest): reviewer and merger profiles cannot consume an author handoff by supplyingrole="author"; a mismatching supplied role is refused; a foreignrequired_profile(dadeschools-author) is refused; the legitimateprgs-authorprofile consumes the handoff with read-after-write ownership evidence.Verification runs (in
branches/fix-issue-843-cross-role-allocation-handoff)Re-review request
Please perform a fresh independent review of PR #845 at the new pinned head
5eb89f883074cf8ab56461767188e9454ad04a98against issue #843 acceptance criteria and the F1 remediation above.Canonical PR State
[THREAD STATE LEDGER] PR #845 — F1 remediation pushed; fresh review requested
What is true now:
What changed:
a6c15afec1→5eb89f8830(feature-branch push); PR body updated via gitea_edit_prWhat is blocked:
Who/what acts next:
5eb89f8830Co-Authored-By: Claude Opus 4.8 (1M context) [email protected]
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:
a6c15afec1target_branch: master
target_branch_sha:
4f3a464a90last_activity: 2026-07-23T19:40:25Z
expires_at: 2026-07-23T19:50:25Z
blocker: obsolete-superseded-or-expired-lease
Canonical obsolete reviewer lease cleanup (#691)
a6c15afec1f0c9ffb25erepo: 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:
f0c9ffb25etarget_branch: master
target_branch_sha:
1c455b6ec0last_activity: 2026-07-23T19:41:29Z
expires_at: 2026-07-23T19:51:29Z
blocker: none
Review verdict: APPROVE — PR #845 (Closes #843)
Independent re-review by sysadmin / prgs-reviewer, distinct from author jcwalker3. Reviewed the live head
f0c9ffb25eonly.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..f0c9ffb2over the five fix files is empty: the merge brought in master only and smuggled no edits into the fix. The security fix content is commit5eb89f8, 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 remediatedReview #515 applies only to the superseded head
a6c15af(Gitea marks itstale). 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):role=(role or active_role).strip() or "author"is removed. The adopter role is nowactive_role = _profile_role_kind(profile)— derived authoritatively from the authenticated profile, never from caller input."author"default).rolethat disagrees with the profile-derived role is rejected (outcome: blocked), no reinterpretation.role=active_roleplusadopter_profile_nameandadopter_namespace(derived from the profile) downstream for provenance validation.Library (
lease_lifecycle.adopt_lease):adopter_role == required_role(raise otherwise) — andadopter_roleis now the authoritative profile-derived role.required_profile/required_namespaceare validated against the caller context; mismatch fails closed.adopted_by_session_idownership evidence emitted.Missing-test gap closed: new
MCPBoundaryAdoptRoleBindingTestproves at the tool boundary that a reviewer profile and a merger profile cannot consume an author handoff viarole="author"(test_reviewer_profile_cannot_consume_author_handoff_via_role_author,test_merger_…), that a reviewer profile is rejected even with noroleargument, that an author profile supplying a mismatching role is rejected, that a foreignrequired_profileis rejected, and that the legitimateprgs-authorprofile still consumes. The original attack path from F1 (anygitea.readnamespace 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 carriesconsume_allocationidentifiers +required_role/required_profile/required_namespaceinstructions; 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)F1 security subset by name (10 passed): MCPBoundaryAdoptRoleBindingTest (7) + wrong_role / terminal / second-adoption rejection.
No new blocker found.
Canonical PR State
Co-Authored-By: Claude Opus 4.8 (1M context) [email protected]
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:
f0c9ffb25etarget_branch: master
target_branch_sha:
1c455b6ec0last_activity: 2026-07-23T19:43:38Z
expires_at: 2026-07-23T19:53:38Z
blocker: manual-release
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:
f0c9ffb25etarget_branch: master
target_branch_sha:
1c455b6ec0last_activity: 2026-07-23T19:56:40Z
expires_at: 2026-07-23T20:06:40Z
blocker: none