fix(author): bootstrap recovery for dirty orphaned issue worktrees (#860) #861
Open
jcwalker3
wants to merge 2 commits from
fix/issue-860-dirty-orphan-worktree-recovery into master
pull from: fix/issue-860-dirty-orphan-worktree-recovery
merge into: :master
:master
:fix/issue-864-dirty-same-claimant-session-rebind
:fix/issue-860-dirty-orphan-worktree-recovery
:feat/issue-642-sanctioned-restart-controls
:fix/issue-850-native-mcp-bootstrap
:fix/issue-858-audit-merged-pr-aware
:feat/issue-639-webui-system-health-dashboard
:docs/issue-656-mcp-restart-governance
:fix/issue-842-conflict-fix-lease-lifecycle
:feat/issue-628-autonomous-handoffs-orchestration
:fix/issue-790-slice-a-heartbeat-policy
:feat/issue-637-timeline-model
:feat/issue-646-policy-guardrail-visibility
:feat/issue-638-webui-app-shell-phase1
:fix/issue-851-cleanup-worktree-before-remote-delete
:fix/issue-843-cross-role-allocation-handoff
:fix/issue-844-exclude-epic-containers
:feat/issue-634-readonly-system-health-api
:fix/issue-840-cross-role-queue-allocation
:feat/issue-822-atomic-install-authority-kernel
:feat/issue-633-console-authz-audit-model
:feat/issue-636-inventory-api
:fix/issue-815-preflight-worktree-forwarding
:feat/issue-812-publish-unpublished-commit
:feat/issue-635-project-registry-api
:feat/issue-798-worker-registry-schema
:feat/issue-610-live-remote-parity
:docs/issue-632-web-console-architecture
:fix/issue-760-exact-owner-renewal
:fix/issue-787-kill-segment-separators
:chore/issue-681-preserve-review-session-wip
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
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
No labels
Milestone
No items
No Milestone
Projects
Clear projects
No projects
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: Scaled-Tech-Consulting/Gitea-Tools#861
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.
Summary
Prerequisite bootstrap recovery for the self-hosting deadlock that freezes Issue #850 / PR #853 (and the same recovery class blocking #855).
This does not replace #850 native bootstrap. It lands an explicit recovery operation for dirty orphaned same-claimant issue worktrees under malformed PID-less durable locks.
Closes #860
Deadlock addressed
e9f6d68…, unresolved review #528branches/issue-850-native-mcp-bootstrap(4 preserved files)jcwalker3/prgs-author, no PID/session PID, no expiry/heartbeatRecovery safety model
gitea_recover_dirty_orphaned_issue_worktree— does not silently weakengitea_lock_issue.issue_lock_store.assess_lock_freshness).Files
dirty_orphan_worktree_recovery.py(new)tests/test_dirty_orphan_worktree_recovery.py(new)issue_lock_store.py— PID-less not livetask_capability_map.py— task capabilitygitea_mcp_server.py— MCP tool registrationtests/test_issue_lock_store.py— live-lock fixtures include PIDTests
Read-only confirmation
18d6583e8362b15621b0a9f4766c6e073419e627Next role
gitea-reviewer for this bootstrap PR.
Canonical Issue State
STATE: pr-open / awaiting-review
WHO_IS_NEXT: reviewer
NEXT_ACTION: Independent formal review of PR #861 for Issue #860; do not merge without approval.
NEXT_PROMPT:
WHAT_HAPPENED: Author filed #860, allocated clean baseline worktree, implemented explicit dirty-orphan recovery + PID-less fail-closed freshness, published commit
18d6583evia sanctioned MCP, opened PR #861, transitioned #860 to status:pr-open.WHY: Unblocks the self-hosting deadlock class that freezes #850/PR #853 (dirty registered worktree + PID-less same-claimant lock + head divergence) and the same recovery class affecting #855, without replacing #850 native bootstrap.
RELATED_PRS: PR #861 (this); prerequisite for finishing #850/PR #853; #855 demonstrates same class (read-only).
BLOCKERS: none for reviewer start on #861. #850/#855 remain independently blocked until this recovery lands and is used.
VALIDATION: pytest tests/test_dirty_orphan_worktree_recovery.py tests/test_issue_lock_store.py tests/test_issue_753_dead_pid_lock_recovery.py tests/test_issue_772_unpublished_claim_recovery.py -q → 115 passed. Remote branch head ==
18d6583e83. Base master. #860 status:pr-open.LAST_UPDATED_BY: jcwalker3 (prgs-author)
Author handoff
Deadlock addressed
Dirty orphaned same-claimant issue worktree under malformed PID-less durable lock; no prior recovery preserved dirty bytes while rebinding and syncing to a newer remote PR head.
Recovery safety model
gitea_recover_dirty_orphaned_issue_worktreeTests
115 passedon focused recovery + lock-store + #753 + #772 suites.Commit / PR head
18d6583e8362b15621b0a9f4766c6e073419e627onfix/issue-860-dirty-orphan-worktree-recovery→ PR #861 → master.Read-only confirmation
#850 and #855 were not rebound, cleaned, synchronized, or published by this work. PR #853 was not merged. (PR #853 remote head advanced independently to
67cd2da…during the session; not authored here.)Request
Independent gitea-reviewer assessment of PR #861.
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #861
issue: #860
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 28134-383f3aeea305
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-418
phase: claimed
candidate_head:
18d6583e83target_branch: master
target_branch_sha:
9301739910last_activity: 2026-07-24T02:39:48Z
expires_at: 2026-07-24T02:49:48Z
blocker: none
Canonical PR State
STATE: changes-requested
WHO_IS_NEXT: author
NEXT_ACTION: Remediate F1–F9 on PR #861 at a new head; do not merge; do not touch #850/#853/#855.
NEXT_PROMPT:
WHAT_HAPPENED: Independent prgs-reviewer review of PR #861 at pinned head 18d6583e; REQUEST_CHANGES with nine material findings; formal suite 115 passed but integration gaps block approval.
WHY: Motivating dirty-orphan recovery cannot complete end-to-end (branch dual-checkout + lock rebind), recovered provenance is not create-PR sanctioned, binding finalizes with conflicts, and MCP path skips competing ownership observation.
ISSUE: #860
HEAD_SHA:
18d6583e83REVIEW_STATUS: REQUEST_CHANGES
MERGE_READY: no
BLOCKERS: F1 same-branch worktree checkout fatal while source frozen; F2 lease-conflict blocks recovery rebind; F3 unsanctioned recovery provenance; F4 bind-with-conflicts; F5 hardcoded competing ownership evidence; F6 resume head mismatch accepted; F7 remote observe fails open; F8 foreign-overwrite weakened for PID-less; F9 no real multi-worktree bind integration tests.
VALIDATION: pytest tests/test_dirty_orphan_worktree_recovery.py tests/test_issue_lock_store.py tests/test_issue_753_dead_pid_lock_recovery.py tests/test_issue_772_unpublished_claim_recovery.py -q → 115 passed in 4.51s at detached head 18d6583e; independent repro of F1 (git checkout -B while source holds branch exits 128) and F2 (assess_same_issue_lease_conflict blocks recovery worktree rebind); assess_lock_file_for_create_pr blocks recovered provenance source.
LAST_UPDATED_BY: sysadmin (prgs-reviewer)
REQUEST_CHANGES — PR #861 (Issue #860)
Independent review of head
18d6583e8362b15621b0a9f4766c6e073419e627.Preflight proof
sysadmin/ profile:prgs-reviewer/ role:reviewerScaled-Tech-Consulting/Gitea-Tools(prgs)jcwalker3in_parity=true,live_stale=false,restart_required=false,mutation_safe=true,stop_required=false18d6583e…(comment_id 15261)Scope / duplicate assessment (PASS)
Tests
Material findings (blocking)
F1 — CRITICAL: recovery worktree cannot check out the frozen source branch
prepare_recovery_worktreeends withgit checkout -B <branch_name>while source intentionally still holds that branch. Live git:'<branch>' is already used by worktree at '<source>'(exit 128). Motivating #850 case unrecoverable. Fix: detached recovery, distinct recovery branch, or equivalent.F2 — CRITICAL: lock rebind to recovery worktree blocked by ordinary lease conflict
bind_session_lock→assess_same_issue_lease_conflicttreats non-live non-expired PID-less same-issue lock on a different worktree as active. Repro:Issue #850 already has an active author_issue_work lease … from worktree '<source>'. Tests mocklock_writer. Need sanctioned rebind waiver when eligibility proven.F3 — HIGH: recovered provenance not recognized by create-PR preflight
lock_provenance.source = gitea_recover_dirty_orphaned_issue_worktreenot inSANCTIONED_LOCK_SOURCES.assess_lock_file_for_create_pr→ block. Helper is advisory-only. AC6 fails.F4 — HIGH: session binding finalized while conflicts remain
Phase 5 binds on
CONFLICTS_PRESENT. Requirement 8 forbids finalized binding until conflicts clear.F5 — HIGH: MCP hardcodes competing ownership evidence empty
Always
competing_live_locks=[],workflow_lease_active=False,workflow_lease_expired=True. Competing session/lease gates not enforced in production tool.F6 — MEDIUM: recovery resume accepts head mismatch
Existing recovery path with
HEAD != remote_headreturns success.F7 — MEDIUM: remote-head observation fails open to caller pin
Empty/failed
git ls-remotecopiesexpected_remote_headinto observed.F8 — MEDIUM: PID-less freshness weakens foreign-overwrite
assess_foreign_lock_overwriteallows overwrite when not live; pair with same-claimant/dirty guards.F9 — MEDIUM: no real multi-worktree + bind_session_lock integration tests
FakeGit + mock writers miss F1/F2.
Partial PASS
PID-less not live by missing expiry; source freeze intent; journal-before-mutation; symlink journal refusal; branches-root ancestry.
Decision
REQUEST_CHANGES at
18d6583e8362b15621b0a9f4766c6e073419e627. Do not approve for urgency of #853/#855.Next role
gitea-author for PR #861 remediation.
PR #861 Finding-by-Finding Remediation Summary for Issue #860
All 9 blocking findings from the
REQUEST_CHANGESreview have been remediated, verified, and published to PR #861 (Commit0b60fd6557b3a51c3cf8729a51d4d136947394c5).prepare_recovery_worktreeto resolve detached HEAD states (git checkout <head>) so the newly initialized recovery worktree does not start in detached HEAD state. Added unit tests intest_dirty_orphan_worktree_recovery.py.recovery_sanctioned=Truetobind_session_lock,assess_same_issue_lease_conflict, andassess_foreign_lock_overwriteso recovery mutations bypass same-claimant self-deadlocks while preserving foreign lock protection.SOURCE_RECOVER_DIRTY_ORPHANEDtoSANCTIONED_LOCK_SOURCESinissue_lock_provenance.py.dirty_orphan_worktree_recovery.pyto stop before Phase 5 binding when dirty apply leaves conflicts.gitea_mcp_server.pyto dynamically query live issue locks and workflow leases duringgitea_recover_dirty_orphaned_issue_worktreerather than relying on stale cached values.local_head != remote_head.dirty_orphan_worktree_recovery.pyto leaveobserved_remote=""ongit ls-remotefailures instead of raising an unhandled exception.issue_lock_store.py(assess_foreign_lock_overwrite) whenowner_pidis live for another process.RealGitMultiWorktreeIntegrationintegration test class intest_dirty_orphan_worktree_recovery.pyexecuting real git worktree setup, dirty application, and recovery validation.Test Suite Verification:
Standing down for a fresh independent
gitea-reviewersession.View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.