Adds read-only /leases and /api/leases routes for issue claim inventory, reviewer PR lease comments, duplicate PR/branch warnings, and local issue lock visibility without acquire/release mutations. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
130 lines
4.8 KiB
Python
130 lines
4.8 KiB
Python
"""HTML views for lease and collision visibility (#433)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import html
|
|
import json
|
|
|
|
from webui.layout import render_page
|
|
from webui.lease_loader import LeaseSnapshot
|
|
|
|
|
|
def _escape(text: str) -> str:
|
|
return html.escape(text, quote=True)
|
|
|
|
|
|
def _warnings_block(snapshot: LeaseSnapshot) -> str:
|
|
warnings = list(snapshot.duplicate_prs) + list(snapshot.duplicate_branches)
|
|
if not warnings:
|
|
return "<p class='muted'>No duplicate PR/branch collisions detected in current inventory.</p>"
|
|
items = "".join(f"<li>{_escape(w.message)}</li>" for w in warnings)
|
|
return f"<ul class='collision-warnings'>{items}</ul>"
|
|
|
|
|
|
def _lock_block(snapshot: LeaseSnapshot) -> str:
|
|
lock = snapshot.issue_lock
|
|
if not lock:
|
|
return "<p class='muted'>No active local issue lock file.</p>"
|
|
return (
|
|
"<pre class='prompt-text'>"
|
|
f"{_escape(json.dumps(lock, indent=2, sort_keys=True))}"
|
|
"</pre>"
|
|
)
|
|
|
|
|
|
def _claims_table(snapshot: LeaseSnapshot) -> str:
|
|
entries = snapshot.claim_inventory.get("entries") or []
|
|
if not entries:
|
|
return "<p class='muted'>No in-progress issue claims in fetched inventory.</p>"
|
|
rows = []
|
|
for entry in entries:
|
|
rows.append(
|
|
"<tr>"
|
|
f"<td>#{entry.get('issue_number')}</td>"
|
|
f"<td><span class='badge badge-{_escape(str(entry.get('status')))}'>"
|
|
f"{_escape(str(entry.get('status')))}</span></td>"
|
|
f"<td>{_escape(str(entry.get('linked_open_pr') or '—'))}</td>"
|
|
f"<td>{entry.get('heartbeat_count', 0)}</td>"
|
|
f"<td>{_escape(', '.join(entry.get('matching_branches') or []) or '—')}</td>"
|
|
f"<td class='muted'>{_escape('; '.join(entry.get('reasons') or []))}</td>"
|
|
"</tr>"
|
|
)
|
|
return (
|
|
"<table class='registry leases'>"
|
|
"<thead><tr><th>Issue</th><th>Claim status</th><th>Open PR</th>"
|
|
"<th>Heartbeats</th><th>Branches</th><th>Notes</th></tr></thead>"
|
|
f"<tbody>{''.join(rows)}</tbody></table>"
|
|
)
|
|
|
|
|
|
def _reviewer_leases_table(snapshot: LeaseSnapshot) -> str:
|
|
if not snapshot.reviewer_leases:
|
|
return (
|
|
"<p class='muted'>No active reviewer PR lease comments found "
|
|
"(marker <code><!-- mcp-review-lease:v1 --></code>; see #407).</p>"
|
|
)
|
|
rows = []
|
|
for lease in snapshot.reviewer_leases:
|
|
rows.append(
|
|
"<tr>"
|
|
f"<td>#{lease.get('pr_number')}</td>"
|
|
f"<td>{_escape(str(lease.get('reviewer_identity') or '—'))}</td>"
|
|
f"<td><code>{_escape(str(lease.get('profile') or '—'))}</code></td>"
|
|
f"<td>{_escape(str(lease.get('phase') or '—'))}</td>"
|
|
f"<td><code>{_escape(str(lease.get('expires_at') or '—'))}</code></td>"
|
|
"</tr>"
|
|
)
|
|
return (
|
|
"<table class='registry leases'>"
|
|
"<thead><tr><th>PR</th><th>Reviewer</th><th>Profile</th>"
|
|
"<th>Phase</th><th>Expires</th></tr></thead>"
|
|
f"<tbody>{''.join(rows)}</tbody></table>"
|
|
)
|
|
|
|
|
|
def _history_links(snapshot: LeaseSnapshot) -> str:
|
|
items = "".join(
|
|
f"<li><a href=\"https://gitea.prgs.cc/Scaled-Tech-Consulting/Gitea-Tools/issues/"
|
|
f"{item['number']}\">#{item['number']}</a> — {_escape(item['title'])}</li>"
|
|
for item in snapshot.collision_history
|
|
)
|
|
return f"<ul>{items}</ul>"
|
|
|
|
|
|
LEASE_PAGE_STYLES = """
|
|
<style>
|
|
.collision-warnings li { color: #f0c4c4; margin: 0.35rem 0; }
|
|
table.leases td:nth-child(6) { font-size: 0.85rem; }
|
|
.badge-active, .badge-awaiting_review { color: #8fd19e; border-color: #3d6b4a; }
|
|
.badge-stale, .badge-phantom, .badge-reclaimable { color: #f0a8a8; border-color: #7a3b3b; }
|
|
</style>
|
|
"""
|
|
|
|
|
|
def render_leases_page(snapshot: LeaseSnapshot) -> str:
|
|
error_block = ""
|
|
if snapshot.fetch_error:
|
|
error_block = (
|
|
'<div class="stub"><p><strong>Partial load:</strong> '
|
|
f"{_escape(snapshot.fetch_error)}</p></div>"
|
|
)
|
|
body = (
|
|
"<h2>Leases & collisions</h2>"
|
|
"<p>Read-only visibility for issue claims, reviewer PR leases, and "
|
|
"duplicate-work risks. Does not acquire or release leases.</p>"
|
|
f"{error_block}"
|
|
f"<p class='meta'>Project: <code>{_escape(snapshot.repo_label)}</code></p>"
|
|
"<h3>Collision warnings</h3>"
|
|
f"{_warnings_block(snapshot)}"
|
|
"<h3>Local issue lock</h3>"
|
|
f"{_lock_block(snapshot)}"
|
|
"<h3>In-progress issue claims (#268)</h3>"
|
|
f"{_claims_table(snapshot)}"
|
|
"<h3>Reviewer PR leases (#407)</h3>"
|
|
f"{_reviewer_leases_table(snapshot)}"
|
|
"<h3>Collision / lease history issues</h3>"
|
|
f"{_history_links(snapshot)}"
|
|
"<p><a href=\"/api/leases\">JSON API</a></p>"
|
|
f"{LEASE_PAGE_STYLES}"
|
|
)
|
|
return render_page(title="Leases", body_html=body) |