"""HTML views for the Gitea issue↔PR linkage console (#645, Phase 3).
Read-only renderer over :mod:`webui.linkage_loader`. The page's job is to make
three things impossible to misread:
* **why** an edge exists — every link carries its evidence badge, so a bare
``#N`` mention never looks like a closing claim;
* **what was not loaded** — a partial inventory, an unfocused thread, or an
unavailable handoff source renders as an explicit qualifier, never as an
affirmative "none";
* **that nothing here mutates** — there is no review, merge, or edit control,
and the deep link out to Gitea appears only under the admin reveal opt-in.
"""
from __future__ import annotations
from html import escape
from typing import Sequence
from webui.layout import render_page
from webui.linkage_loader import (
EVIDENCE_BRANCH,
EVIDENCE_CLOSES,
EVIDENCE_DESCRIPTIONS,
EVIDENCE_ORDER,
EVIDENCE_REFERENCE,
HANDOFF_LOADED,
HANDOFF_NOT_LOADED,
HandoffSummary,
LinkageNode,
LinkageSnapshot,
)
_EVIDENCE_CSS = {
EVIDENCE_CLOSES: "badge-health-ok",
EVIDENCE_BRANCH: "badge-health-skipped",
EVIDENCE_REFERENCE: "badge-health-unproven",
}
_EVIDENCE_LABEL = {
EVIDENCE_CLOSES: "closes",
EVIDENCE_BRANCH: "branch",
EVIDENCE_REFERENCE: "mention",
}
def _badge(text: str, css: str) -> str:
return f'{escape(text)}'
def _labels(names: Sequence[str]) -> str:
if not names:
return '—'
return " ".join(_badge(name, "badge-health-skipped") for name in names)
def _ref(node: LinkageNode) -> str:
"""Render an item reference, hyperlinked only when deep links are revealed."""
label = f"#{node.number}"
if node.deep_link:
return f'{escape(label)}'
return f"{escape(label)}"
def _scope_card(snapshot: LinkageSnapshot) -> str:
focus = (
"none"
if snapshot.focus is None
else f"{snapshot.focus[0]}#{snapshot.focus[1]}"
)
completeness = (
_badge("complete", "badge-health-ok")
if snapshot.inventory_complete
else _badge("partial", "badge-health-degraded")
)
links_note = (
"Every linkage edge below is a claim about this loaded window only."
if snapshot.inventory_complete
else (
"Pagination did not complete for this window, so an empty link list "
"means none found in what was loaded — not that no link exists."
)
)
deep_links = (
_badge("enabled", "badge-health-ok")
if snapshot.deep_links_enabled
else _badge("hidden", "badge-health-skipped")
)
return f"""
| Project | {escape(snapshot.project_id or "—")} |
|---|---|
| Repository | {escape(snapshot.repo_label or "—")} |
| Item state | {escape(snapshot.state_scope)} |
| Focused thread | {escape(focus)} |
| Inventory | {completeness} |
| Gitea deep links | {deep_links} |
{links_note}
#{number}" for number in snapshot.contested_issues)
return (
'#{number}" for number in node.linked_prs)
if node.contested:
linked += " " + _badge("contested", "badge-blocked")
elif node.links_authoritative:
linked = 'none'
else:
# The distinction an operator needs: nothing found in a window that
# was not fully loaded is not the same as nothing existing.
linked = 'none found (partial inventory)'
rows.append(
"#{link.issue_number} "
f"{_evidence_badges(link.evidence)}{escape(snapshot.handoff_status.reason or "")}
Add ?issue=N or ?pr=N to load the latest
Canonical Thread Handoff for one thread.
{_badge("unavailable", "badge-health-degraded")} {escape(snapshot.handoff_status.reason or "handoff source did not run")}. This is not evidence that the thread carries no handoff.
Comments loaded; no Canonical Thread Handoff comment found on this thread.
The comment\'s heading is not a declared CTH type, ' "so it is reported as unrecognized rather than republished.
" ) ) return f"""| Status | {escape(handoff.status or "—")} |
|---|---|
| Next owner | {escape(handoff.next_owner or "—")} |
| Current blocker | {escape(handoff.current_blocker or "—")} |
| Decision | {escape(handoff.decision or "—")} |
| Next action | {escape(handoff.next_action or "—")} |
Full event history:
/api/v1/timeline
GITEA_MCP_REVEAL_ENDPOINTS admin opt-in is set."
if snapshot.deep_links_enabled
else (
"Gitea deep links are withheld. Set "
"GITEA_MCP_REVEAL_ENDPOINTS=1 server-side to reveal "
"them; item numbers stay usable without them."
)
)
return f"""{reveal_note}
Read-only surface: no issue or PR editing, no review, and no merge.
JSON export: /api/v1/gitea/linkage
| Issue | Title | State | Labels | Linked PRs | Latest handoff |
|---|
| PR | Title | State | Labels | Linked issues | Latest handoff |
|---|