feat(lease): make the author task heartbeat load-bearing (Closes #790, Slice A) #794
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#794
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 #790
Follow-up: #792
Follow-up: #793
This pull request implements Slice A only
Issue #790 was reduced to Slice A by the scope transfer recorded in its comment 14010, so the closing reference above is accurate. The rest of the original scope was moved to dedicated issues before this pull request was opened:
lock_generationverification inverify_lock_for_mutation, the Issue #760 renewal re-scope, remaining task-class policy alignment, control-plane lease alignment.Merging this closes #790 correctly. It does not complete #792 or #793, and #790 should not be reopened to represent them.
Problem
issue_lock_store.assess_lock_freshnessparsedlast_heartbeat_atand then never consulted it. Liveness was decided by an absolute four-hourexpires_atand by process-identifier liveness, and the recorded identifier is the long-lived MCP daemon rather than the authoring task, so an abandoned claim stayed live for the full four hours. A tree-wide search found the field written in exactly one place,gitea_mcp_server._build_author_issue_work_lease, and advanced by nothing anywhere.Issue #787 / PR #789 hit this. Issue #760 / PR #791 hit it again: its lease was minted at
04:19:08Z, never heartbeated, its recorded pid 39849 stayed alive as a daemon, and reconciliation was still blocked at06:39:43Z— more than five hours after the work landed on master.What changed
A1 — one authoritative policy source. New
lease_policydeclares every duration for every task class: author initial and sliding TTL 10 minutes, heartbeat cadence 2, stale warning 5, missed-heartbeat grace 10, absolute cap 8 hours, recovery grace 10, terminal race-drain 2. It ships first so the first heartbeat and TTL behavior to run reads from it (AC-N7). The duplicated four-hour literal is deleted from bothissue_lock_storeandgitea_mcp_server. Reviewer, merger, and conflict-fix classes are declared but not rewired; a test asserts the declared numbers still match the constantsreviewer_pr_leaseandpr_work_leaseown, so the two cannot drift apart before #793 migrates those call sites.A2 — load-bearing freshness, with two intentional asymmetries. An alive process identifier never establishes freshness anywhere (AC-N2): it is recorded as evidence and no branch returns live because of it. A dead one still marks a lease stale, and that band still precedes every heartbeat evaluation, so Issue #753 dead-session recovery keys on exactly the classification it always did. New bands
stale_missed_heartbeatandstale_absolute_capare classified inbranch_cleanup_guardinstead of falling through to unknown-status, and still block unless the ownership record provesreclaim_allowed is True. A heartbeat lease carrying no heartbeat is contradictory and fails closed.assess_expired_lock_reclaimaccepts a lapsed heartbeat as reclaim grounds for heartbeat-lifecycle leases only — under this lifecycle the heartbeat is the liveness proof, and additionally demanding a dead process would reinstate the original defect.A3 and A4 — task-session identity and the writer.
mint_task_session_idproduces an ownership key containing no process identifier, because the daemon identifier is reused by every task it serves and identifies none of them.heartbeat_session_lockwrites inside the existing per-issue flock under the Issue #772 generation compare-and-swap, verifying exact issue, branch, realpath-normalized worktree, claimant username, claimant profile, and recorded session identifier. It cannot acquire, take over, or revive: a lease past its grace is refused and must use the reclaim path, so a session that stopped proving liveness cannot restore ownership retroactively. Newgitea_heartbeat_issue_lockgates on the authoritylock_issuealready requires, being a strictly narrower operation.A5 — legacy compatibility (AC-N8). An explicit
lifecycle_versionmarker, never a timestamp comparison, discriminates legacy from heartbeat leases. A legacy lock haslast_heartbeat_atmatchingcreated_atforever precisely because nothing advanced it, and a freshly minted heartbeat lease has them matching too, so that equality carries no information in either direction. Legacy locks keep their recorded absolute expiry and are never evaluated against the short grace, so deploying this cannot make an existing claim instantly reclaimable. They leave that state only through terminal retirement (#792) orrebind_legacy_lock, which re-verifies the exact owner and mints a genuine identifier and first heartbeat while preserving the original claim underlegacy_origin. Rebinding a lapsed legacy lease is refused; that case belongs to Issue #760 renewal or Issue #601 reclaim.A6 — native coverage. Review #499 proved assessor-level tests miss discard points, so
tests/test_issue_790_heartbeat_mcp_path.pydrives the real tools against a real git repository and a real durable lock.Files changed
lease_policy.pyissue_lock_store.pyheartbeat_session_lock,rebind_legacy_lockgitea_mcp_server.pygitea_heartbeat_issue_locktoolbranch_cleanup_guard.pytask_capability_map.pyheartbeat_issue_lockcapability entrydocs/mcp-tool-inventory.mdtests/test_issue_790_lease_policy.pytests/test_issue_790_heartbeat_mcp_path.py1974 insertions, 32 deletions.
Non-regressions preserved
Issue #760 exact-owner renewal is untouched and stays in force until #793 replaces it end to end. Issue #753 dead-session recovery, Issue #755 owning-pull-request evidence, Issue #772 generation compare-and-swap, and Issue #747 reviewer sliding TTL all keep their existing behavior, each covered by its own suite below.
Tests and results
Run with
venv/bin/python -m pytestfrom the allocated worktree.test_issue_790_lease_policy.py40,test_issue_790_heartbeat_mcp_path.py15).test_issue_lock_store,test_lease_lifecycle,test_issue_753_dead_pid_lock_recovery,test_issue_755_owning_pr_recovery,test_issue_760_exact_owner_lease_renewal,test_issue_760_mcp_renewal_path,test_issue_768_strict_descendant_recovery,test_issue_772_unpublished_claim_recovery,test_lock_issue_mcp_registration,test_issue_lock_worktree,test_issue_lock_adoption,test_issue_work_duplicate_gate,test_branch_cleanup_guard,test_task_capability_role_invariants,test_issue_claim_heartbeat,test_worktrees) — 383 passed, 98 subtests passed.Baseline comparison: a clean detached worktree at
branches/baseline-master-issue-790, pinned to master620ed6e9a9550b8da2ceb82d9ab8744e8920490f, reports 11 failed, 4240 passed, 6 skipped, 493 subtests passed running the identical command. The eleven failing node identifiers are byte-for-byte the ones on this branch: six intest_commit_payloads.py, two intest_issue_702_review_findings_f1_f6.py, and one each intest_mcp_server.py::TestPreflightVerification,test_post_merge_moot_lease.py, andtest_reconciler_supersession_close.py. None touch lease, lock-freshness, or heartbeat code. The 55-test delta between 4240 and 4295 is exactly the new suites, so this branch adds no new failures. Both worktrees reported a zero-entrygit status --porcelainbefore and after validation.Risk
Behavioral change to the author lock lifecycle, so the risk concentrates in two places and both are covered.
Shortening the author TTL from 240 to 10 minutes could in principle make deployed claims reclaimable at restart. It cannot: every lock already on disk lacks the lifecycle marker and is therefore held to its recorded absolute expiry, and the short grace applies only to leases minted after this lands.
Loosening reclaim could in principle weaken a guard. The new reclaim ground is reachable only for heartbeat-lifecycle leases, never for a legacy lock, and every other precondition — cleanliness, ancestry, publication, worktree binding, the duplicate-work gate, the compare-and-swap — is unchanged.
One consequence worth stating plainly for review: an author session that runs longer than 10 minutes without calling
gitea_heartbeat_issue_lockwill have its claim become reclaimable. That is the intended behavior, and it is why the heartbeat cadence is 2 minutes, giving five missed beats before the grace lapses.Known limitations
The heartbeat is a tool an author session must call; nothing in this slice schedules it automatically. Reviewer, merger, conflict-fix, and control-plane leases still run their own lifecycles until #793.
verify_lock_for_mutationstill does not check the generation, which is also #793.Provenance
fix/issue-790-slice-a-heartbeat-policy/Users/jasonwalker/Development/Gitea-Tools/branches/issue-790-slice-a-heartbeat-policy243f52dc7959c362242ebea321fafa3950f13083620ed6e9a9550b8da2ceb82d9ab8744e8920490fCTH: Author Handoff
Status: published_awaiting_independent_review
Next owner: reviewer (prgs-reviewer / sysadmin)
Current blocker: none
Decision: PR #794 implements Slice A of #790 only; ready for independent review
Proof: head
243f52dc7959c362242ebea321fafa3950f13083onfix/issue-790-slice-a-heartbeat-policy; base master; Closes #790; follow-ups #792/#793 are out of scopeNext action: independent formal review at exact head
243f52dc7959c362242ebea321fafa3950f13083Ready-to-paste prompt: see NEXT_PROMPT below
Canonical Issue State
STATE: pr_open_awaiting_review
WHO_IS_NEXT: reviewer
NEXT_ACTION: formal independent review of PR #794 at head
243f52dc79NEXT_PROMPT:
WHAT_HAPPENED: Canonical author handoff posted for already-published PR #794 (incomplete handoff). Implementation and validation evidence are in the PR body.
WHY: Queue hygiene — open PR lacked CTH handoff so reviewer ownership was unclear.
RELATED_PRS: #794 (this PR); follow-ups #792, #793
BLOCKERS: none
VALIDATION: See PR body — new suites 55 passed; lock/lease regression 383 passed; full suite 4295 passed / 11 baseline fail vs master 4240/11.
LAST_UPDATED_BY: jcwalker3 (prgs-author)
REPOSITORY: Scaled-Tech-Consulting/Gitea-Tools
ISSUE: #790
PR: #794
HEAD_SHA:
243f52dc79BASE_BRANCH: master
ACTING_ROLE: author
ACTING_IDENTITY: jcwalker3
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #794
issue: #790
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 89687-276bb9edcbbf
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-794
phase: claimed
candidate_head:
243f52dc79target_branch: master
target_branch_sha:
620ed6e9a9last_activity: 2026-07-22T10:52:21Z
expires_at: 2026-07-22T11:02:21Z
blocker: none
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #794
issue: #790
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 50914-eb6bb39eb08a
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-794
phase: claimed
candidate_head:
243f52dc79target_branch: master
target_branch_sha: none
last_activity: 2026-07-22T11:05:47Z
expires_at: 2026-07-22T11:15:47Z
blocker: none
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #794
issue: #790
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 50914-eb6bb39eb08a
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-794
phase: validation
candidate_head:
243f52dc79target_branch: master
target_branch_sha: none
last_activity: 2026-07-22T11:07:43Z
expires_at: 2026-07-22T11:17:43Z
blocker: none
Canonical PR State
STATE: changes-requested
WHO_IS_NEXT: author
NEXT_ACTION: Fix assess_same_issue_lease_conflict so heartbeat-lifecycle non-live bands (stale_missed_heartbeat, stale_absolute_cap) enter the reclaim path even when expires_at is still in the future; add regression tests; re-request review.
NEXT_PROMPT:
WHAT_HAPPENED: Independent review of PR #794 (Slice A author heartbeat policy) by sysadmin/prgs-reviewer.
WHY: assess_expired_lock_reclaim allows reclaim for the new stale bands, but assess_same_issue_lease_conflict still keys foreign reclaim on is_lease_expired (expires_at only). Absolute-cap with a recent heartbeat, and TTL>grace env overrides, leave foreign reclaim blocked as active.
ISSUE: #790
HEAD_SHA:
243f52dc79REVIEW_STATUS: REQUEST_CHANGES
MERGE_READY: no
BLOCKERS: conflict gate does not honor heartbeat-lifecycle non-live reclaim bands when expires_at is still future
VALIDATION: pytest test_issue_790_lease_policy + test_issue_790_heartbeat_mcp_path = 55 passed; lock/lease/#753/#760/branch_cleanup/capability regression = 173 passed; manual probes confirmed absolute-cap and TTL>grace conflict-gate hole
LAST_UPDATED_BY: sysadmin (prgs-reviewer)
Independent review detail
Verdict: REQUEST_CHANGES
One correctness hole in the acquisition conflict gate relative to the new freshness/reclaim semantics. Everything else in Slice A looks solid.
What looks good
Default-path missed-heartbeat reclaim works when expires_at and grace align (defaults 10/10).
Blocking: conflict gate still keys off expires_at only
assess_expired_lock_reclaim allows reclaim for stale_missed_heartbeat and stale_absolute_cap without a dead PID.
assess_same_issue_lease_conflict only enters that reclaim branch when is_lease_expired (expires_at only). When freshness is non-live and expires_at is still future, foreign acquisition is rejected as active.
Reproduced on this head:
assess_foreign_lock_overwrite already uses is_lease_live; the same-issue path is inconsistent. gitea_lock_issue raises before bind.
Suggested fix: for heartbeat-lifecycle leases, enter reclaim/renewal when not is_lease_live (or status in the new bands), not only when is_lease_expired. Keep legacy on absolute expires_at. Add regression tests for the three cases above.
Non-blocking
Happy to re-review once the conflict-gate wiring matches the new reclaim bands.
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #794
issue: #790
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 25383-d6b9f0ae2bc8
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-794
phase: claimed
candidate_head:
dc1d0e045ftarget_branch: master
target_branch_sha:
4f3a464a90last_activity: 2026-07-23T08:15:00Z
expires_at: 2026-07-23T08:25:00Z
blocker: none
Canonical PR State
STATE: changes-requested
WHO_IS_NEXT: author
NEXT_ACTION: In issue_lock_store.assess_same_issue_lease_conflict, gate the reclaim/renewal branch on heartbeat-lifecycle non-liveness (not is_lease_live, or freshness status in stale_missed_heartbeat/stale_absolute_cap) rather than is_lease_expired alone; keep legacy leases on absolute expires_at. Add regression tests; re-request review.
NEXT_PROMPT:
WHAT_HAPPENED: Independent re-review of PR #794 (Slice A author heartbeat policy) at current head
dc1d0e0by sysadmin/prgs-reviewer. Prior review #502 (REQUEST_CHANGES at243f52dc) is stale after the head advance; this decision is recorded at the current head.WHY: The single blocking finding from review #502 is unresolved at
dc1d0e0. assess_same_issue_lease_conflict still keys its reclaim branch on is_lease_expired (expires_at only), so a heartbeat-lifecycle lease that is non-live (stale_absolute_cap, or stale_missed_heartbeat under TTL>grace) but whose expires_at is still future is rejected as an active lease and never reaches assess_expired_lock_reclaim, which now correctly permits those bands. assess_foreign_lock_overwrite already uses is_lease_live; the same-issue path is inconsistent.ISSUE: #790
HEAD_SHA:
dc1d0e045fREVIEW_STATUS: REQUEST_CHANGES
MERGE_READY: no
BLOCKERS: assess_same_issue_lease_conflict does not honor heartbeat-lifecycle non-live reclaim bands when expires_at is still future
VALIDATION: Independent source verification at head
dc1d0e0via gitea_get_file(issue_lock_store.py). is_lease_expired (L695-699) = expires_at<=now only; conflict gate (L1044) branches on is_lease_expired; assess_expired_lock_reclaim (L952-972) grants reclaim for stale_missed_heartbeat/stale_absolute_cap but is only consulted inside the is_lease_expired branch; assess_foreign_lock_overwrite (L1092) uses is_lease_live. stale_absolute_cap is reachable at default policy. No fresh pytest run this cycle; this is a static-trace re-confirmation of review #502.LAST_UPDATED_BY: sysadmin (prgs-reviewer)
Independent re-review at head
dc1d0e0Verdict: REQUEST_CHANGES
The head advanced
243f52dc->dc1d0e0after review #502, but the single blocking correctness hole is unchanged in the source, and no author remediation for it was announced.Blocking (unresolved from #502): conflict gate keys on expires_at, not liveness
assess_same_issue_lease_conflictenters the reclaim/renewal branch only underif is_lease_expired(existing_lock)(expires_at <= now). For a heartbeat-lifecycle lease that is non-live but whoseexpires_atis still in the future:expires_at = last_heartbeat + TTLin the future, yet freshness = stale_absolute_cap / live=False.In both cases
is_lease_expiredis False, so the gate falls through to the foreign branch and returns "already has an active lease (fail closed)".assess_expired_lock_reclaim— which now correctly returns reclaim_allowed for exactly these bands (L952-972) — is never reached from this acquisition path.assess_foreign_lock_overwrite(L1092) already usesis_lease_live, so the same-issue and foreign-overwrite gates disagree on the same lock.Effect: the load-bearing heartbeat is not load-bearing for foreign reclaim through the same-issue acquisition path — the precise abandonment scenario #790 exists to fix.
Suggested fix
For heartbeat-lifecycle leases, enter the reclaim/renewal branch when
not is_lease_live(existing_lock)(or freshness status in {stale_missed_heartbeat, stale_absolute_cap}), not only whenis_lease_expired. Keep legacy leases keyed on absoluteexpires_at. Add the three regression cases in NEXT_PROMPT.What looks good (unchanged strengths from #502)
Scope discipline (Slice A only; #792/#793 carved out); lease_policy as the AC-N7 duration source; lifecycle marker discrimination (AC-N8) rather than timestamp heuristics; freshness asymmetries (AC-N2: alive PID never grants live, dead PID still stale for #753); assess_expired_lock_reclaim correctly honoring the new bands. The remaining fix is localized to one gate.
Non-blocking (carried from #502, still open)
4f3a464a— re-run the new suites on current master before merge.repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #794
issue: #790
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 25383-d6b9f0ae2bc8
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr-794
phase: released
candidate_head:
dc1d0e045ftarget_branch: master
target_branch_sha:
4f3a464a90last_activity: 2026-07-23T08:16:54Z
expires_at: 2026-07-23T08:26:54Z
blocker: manual-release
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #794
issue: #790
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: codex-automation-gitea-tools-reviewer-20260724
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr794-6a636c58-automation
phase: claimed
candidate_head:
6a636c58e7target_branch: master
target_branch_sha:
44fe8d2eedlast_activity: 2026-07-24T09:43:31Z
expires_at: 2026-07-24T09:53:31Z
blocker: none
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #794
issue: #790
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: codex-automation-gitea-tools-reviewer-20260724
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr794-6a636c58-automation
phase: validation
candidate_head:
6a636c58e7target_branch: master
target_branch_sha:
44fe8d2eedlast_activity: 2026-07-24T09:44:10Z
expires_at: 2026-07-24T09:54:10Z
blocker: none
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #794
issue: #790
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: codex-automation-gitea-tools-reviewer-20260724
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr794-6a636c58-automation
phase: pre-review-mutation
candidate_head:
6a636c58e7target_branch: master
target_branch_sha:
44fe8d2eedlast_activity: 2026-07-24T09:45:30Z
expires_at: 2026-07-24T09:55:30Z
blocker: none
Canonical PR State
STATE: changes-requested
WHO_IS_NEXT: author
NEXT_ACTION: Correct the same-issue reclaim gate for heartbeat-lifecycle non-live leases, add the three requested regression cases, validate, and re-request review.
NEXT_PROMPT:
WHAT_HAPPENED: Independent reviewer re-evaluated PR #794 at current head
6a636c58e7after the earlier request-changes head became stale.WHY: The branch advanced by merging master, but the blocking same-issue reclaim condition and missing regression cases remain unchanged.
ISSUE: #790
HEAD_SHA:
6a636c58e7REVIEW_STATUS: REQUEST_CHANGES
MERGE_READY: no
BLOCKERS: issue_lock_store.assess_same_issue_lease_conflict still enters reclaim only when expires_at has passed, so heartbeat-lifecycle non-live leases with future expiry are incorrectly treated as active; Gitea reports mergeable=false.
VALIDATION: Unmodified detached head; venv/bin/python -m pytest tests/test_issue_790_lease_policy.py tests/test_issue_790_heartbeat_mcp_path.py -q => 55 passed, 6 subtests passed; git diff --check refs/remotes/prgs/master...HEAD => exit 0; worktree status empty after validation.
LAST_UPDATED_BY: sysadmin (prgs-reviewer)
CTH: Reviewer Handoff
Status: changes_requested
Next owner: author (gitea-author / prgs-author / jcwalker3)
Current blocker: same-issue acquisition does not honor heartbeat-lifecycle non-live reclaim bands while expires_at is future.
Decision: REQUEST_CHANGES at exact head
6a636c58e7.Proof: current-head source trace shows if is_lease_expired(existing_lock) guarding assess_expired_lock_reclaim, while assess_foreign_lock_overwrite uses is_lease_live; requested future-expiry regression cases remain absent; focused validation passed 55 tests and 6 subtests.
Next action: author remediation only; do not merge.
Canonical Handoff
Author remediation status (review #544 REQUEST_CHANGES)
Done at head
b6a8989The blocking same-issue reclaim gate fix is already published on this branch:
issue_lock_store.assess_same_issue_lease_conflictnow enters reclaim/renewal when a heartbeat-lifecycle lease is non-live even ifexpires_atis still in the future (heartbeat_non_live_reclaimable).tests/test_issue_794_conflict_gate_reclaim.pycovers:stale_absolute_cap+ future expiry → foreign reclaim allowedstale_missed_heartbeat+ TTL>grace + future expiry → foreign reclaim allowedValidation (this session)
Remaining (not the RC code blocker)
gitea_mcp_server.py/issue_lock_store.py/task_capability_map.pybecause master gained #860 recovery paths while this PR carries #790 heartbeat freshness. That is a separate integration step after re-review of the RC fix.mergeable=falsefor that reason.Who is next
reviewer — fresh independent review at exact head
b6a8989c7749426f16839d04a244b16c7a50d4fe(prior RC was at6a636c58). Do not reuse review #544.repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #794
issue: #790
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 42336-4c26d39b8621
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr794-heartbeat-20260724091225
phase: claimed
candidate_head:
0987f93c67target_branch: master
target_branch_sha:
103d0df289last_activity: 2026-07-24T13:12:35Z
expires_at: 2026-07-24T13:22:35Z
blocker: none
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #794
issue: #790
reviewer_identity: sysadmin
profile: prgs-reviewer
session_id: 42336-4c26d39b8621
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/review-pr794-heartbeat-20260724091225
phase: validated
candidate_head:
0987f93c67target_branch: master
target_branch_sha:
103d0df289last_activity: 2026-07-24T13:13:43Z
expires_at: 2026-07-24T13:23:43Z
blocker: none
Canonical PR State
STATE: approved
WHO_IS_NEXT: merger
NEXT_ACTION: Merger adopt lease and merge at exact head
0987f93c67after gitea_assess_pr_sync_status returns merge_now.NEXT_PROMPT:
WHAT_HAPPENED: Independent re-review of PR #794 Slice A at current head after prior REQUEST_CHANGES (reviews #502/#516/#544) became stale; author advanced head with conflict-gate fix
b6a8989and regression suite.WHY: Prior blocker resolved — assess_same_issue_lease_conflict now enters reclaim/renewal for heartbeat-lifecycle non-live leases even when expires_at is future; legacy still on absolute expires_at; three requested cases + live/legacy guards covered in tests/test_issue_794_conflict_gate_reclaim.py.
ISSUE: #790
HEAD_SHA:
0987f93c67REVIEW_STATUS: APPROVED
MERGE_READY: true
BLOCKERS: none for review; merge is merger-owned
VALIDATION: cwd branches/review-pr794-heartbeat-20260724091225; HEAD=0987f93c; clean before/after; pytest test_issue_790_lease_policy + test_issue_790_heartbeat_mcp_path + test_issue_794_conflict_gate_reclaim = 60 passed, 6 subtests; lock regression 169 passed with 6 branch_cleanup failures that match master baseline
103d0dfbyte-for-byte; git merge-base --is-ancestor head master exit 1 (not already landed).NATIVE_REVIEW_PROOF: transport=native_mcp; entrypoint=mcp_server; token_fingerprint=47328c7c1075ef27
LAST_UPDATED_BY: sysadmin (prgs-reviewer)
CTH: Reviewer Handoff
Status: approved
Next owner: merger (gitea-merger / prgs-merger)
Current blocker: none for review
Decision: APPROVED at exact head
0987f93c67Proof: conflict-gate fix at b6a8989; test_issue_794_conflict_gate_reclaim covers stale_absolute_cap future expires foreign reclaim, stale_missed_heartbeat TTL>grace foreign reclaim, live still blocks foreign; baseline-equivalent 6 branch_cleanup failures on master; focused 60 passed.
Next action: merger merge sequence only; do not author-update.
Canonical Handoff
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #794
issue: #790
reviewer_identity: sysadmin
profile: prgs-merger
session_id: 42332-e254a1a18f05
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/merge-pr794-20260724092533
phase: claimed
candidate_head:
0987f93c67target_branch: master
target_branch_sha:
103d0df289last_activity: 2026-07-24T13:25:55Z
expires_at: 2026-07-24T13:35:55Z
blocker: none
Stale #332 review-decision lock cleanup (#594)
Status: APPLIED
sysadminprgs-merger2026-07-24T13:26:12.480900+00:00approveon PR feat(lease): make the author task heartbeat load-bearing (Closes #790, Slice A) (#794)closed(merged=True)2e4ed38c51d6372555389e9cf7815354f903a8031prgs-reviewerManual deletion of session-state files is not the workflow.
This path only clears a lock when the referenced PR is merged/closed.
repo: Scaled-Tech-Consulting/Gitea-Tools
pr: #794
issue: #790
reviewer_identity: sysadmin
profile: prgs-merger
session_id: 42332-e254a1a18f05
worktree: /Users/jasonwalker/Development/Gitea-Tools/branches/merge-pr794-20260724092533
phase: released
candidate_head:
0987f93c67target_branch: master
target_branch_sha:
103d0df289last_activity: 2026-07-24T13:29:50Z
expires_at: 2026-07-24T13:39:50Z
blocker: post-merge-moot