issue_detail.html
html
sha256:f99af7b1a7f36c4d537d1c630d4b71fc39222b1255f82e930929e2fc89015e11
fix: relax browse_repo perf budget to 500ms — 200ms was too…
Sonnet 4.6
99 days ago
| 1 | {% extends "musehub/base.html" %} |
| 2 | {% from "musehub/macros/label.html" import label_chip %} |
| 3 | |
| 4 | {%- set state_cls = "open" if issue.state == "open" else "closed" %} |
| 5 | |
| 6 | {% block title %}#{{ issue.number }} · {{ issue.title }} — {{ owner }}/{{ repo_slug }}{% endblock %} |
| 7 | |
| 8 | {% block breadcrumb %} |
| 9 | <a href="/{{ owner }}">{{ owner }}</a> / |
| 10 | <a href="{{ base_url }}">{{ repo_slug }}</a> / |
| 11 | <a href="{{ base_url }}/issues">issues</a> / #{{ issue.number }} |
| 12 | {% endblock %} |
| 13 | |
| 14 | {% block repo_nav %}{% include "musehub/partials/repo_nav.html" %}{% endblock %} |
| 15 | |
| 16 | {% block page_json %}{"page": "issue-detail"}{% endblock %} |
| 17 | |
| 18 | {# Full-bleed hero layout — same pattern as proposal_detail #} |
| 19 | {% block container_extra_class %} page-container{% endblock %} |
| 20 | |
| 21 | {% block content %} |
| 22 | <div class="isd-page"> |
| 23 | |
| 24 | {# ── Hero ─────────────────────────────────────────────────────────────────── #} |
| 25 | <div class="isd-hero isd-hero--{{ state_cls }}"> |
| 26 | <div class="isd-hero-glow isd-hero-glow--{{ state_cls }}"></div> |
| 27 | <div class="isd-hero-inner"> |
| 28 | |
| 29 | {# Eyebrow: left = state + number + type + labels | right = filed by + time #} |
| 30 | <div class="isd-eyebrow"> |
| 31 | <div class="isd-eyebrow-left"> |
| 32 | <span class="isd-state-badge isd-state-badge--{{ state_cls }}"> |
| 33 | {%- if issue.state == "open" %} |
| 34 | {{ icon("dot", 10) }} |
| 35 | Open |
| 36 | {%- else %} |
| 37 | {{ icon("check", 10) }} |
| 38 | Closed |
| 39 | {%- endif %} |
| 40 | </span> |
| 41 | <span class="isd-issue-num">#{{ issue.number }}</span> |
| 42 | {%- if issue_type %} |
| 43 | <span class="isd-type-pill" style="color:{{ issue_type[2] }};border-color:{{ issue_type[2] }}33;background:{{ issue_type[2] }}18"> |
| 44 | {%- if issue_type[0] == 'bug' %} |
| 45 | {{ icon("bug", 11) }} |
| 46 | {%- elif issue_type[0] == 'feature' %} |
| 47 | {{ icon("star", 11) }} |
| 48 | {%- elif issue_type[0] == 'enhancement' %} |
| 49 | {{ icon("chevrons-up", 11) }} |
| 50 | {%- elif issue_type[0] == 'question' %} |
| 51 | {{ icon("help-circle", 11) }} |
| 52 | {%- elif issue_type[0] == 'documentation' %} |
| 53 | {{ icon("book", 11) }} |
| 54 | {%- elif issue_type[0] == 'performance' %} |
| 55 | {{ icon("zap", 11) }} |
| 56 | {%- elif issue_type[0] == 'discussion' %} |
| 57 | {{ icon("message-square", 11) }} |
| 58 | {%- endif %} |
| 59 | {{ issue_type[1] }} |
| 60 | </span> |
| 61 | {%- endif %} |
| 62 | {%- for lbl in display_labels %} |
| 63 | {%- set lbl_obj = labels_data | selectattr('name', 'equalto', lbl) | first | default({'color': '#8b949e'}) %} |
| 64 | {{ label_chip(lbl, lbl_obj.color, href=base_url ~ '/issues?label=' ~ (lbl | urlencode)) }} |
| 65 | {%- endfor %} |
| 66 | </div> |
| 67 | |
| 68 | <div class="isd-eyebrow-right"> |
| 69 | <span class="isd-eyebrow-filed-label">filed by</span> |
| 70 | <img src="/avatars/handle/{{ issue.author | urlencode }}.svg" class="isd-eyebrow-sigil" width="20" height="20" alt="" aria-hidden="true"> |
| 71 | <a href="/{{ issue.author }}" class="isd-actor-link">{{ issue.author or "unknown" }}</a> |
| 72 | <span class="isd-identity-badge isd-identity-badge--{{ author_identity_type }}">{{ author_identity_type }}</span> |
| 73 | <span class="isd-eyebrow-sep">·</span> |
| 74 | <span class="isd-eyebrow-time">{{ issue.created_at | fmtrelative }}</span> |
| 75 | {%- if issue.closed_at %} |
| 76 | <span class="isd-eyebrow-sep">·</span> |
| 77 | <span class="isd-eyebrow-time">closed {{ issue.closed_at | fmtrelative }}</span> |
| 78 | {%- endif %} |
| 79 | {%- if issue.assignee and issue.assignee != issue.author %} |
| 80 | <span class="isd-eyebrow-sep">·</span> |
| 81 | <span class="isd-eyebrow-filed-label">assigned to</span> |
| 82 | <a href="/{{ issue.assignee }}" class="isd-actor-link">{{ issue.assignee }}</a> |
| 83 | {%- endif %} |
| 84 | </div> |
| 85 | </div> |
| 86 | |
| 87 | <h1 class="isd-title">{{ issue.title }}</h1> |
| 88 | |
| 89 | </div> |
| 90 | |
| 91 | {# Stats strip — anchors | blast | churn | proposals #} |
| 92 | {%- set _blast_total = namespace(v=0) %} |
| 93 | {%- set _churn30_total = namespace(v=0) %} |
| 94 | {%- for _ai in anchor_intel.values() %} |
| 95 | {%- if _ai.blast is defined and _ai.blast is not none %} |
| 96 | {%- set _blast_total.v = _blast_total.v + (_ai.blast | int) %} |
| 97 | {%- endif %} |
| 98 | {%- if _ai.churn_30d is defined and _ai.churn_30d is not none %} |
| 99 | {%- set _churn30_total.v = _churn30_total.v + (_ai.churn_30d | int) %} |
| 100 | {%- endif %} |
| 101 | {%- endfor %} |
| 102 | <div class="isd-stats"> |
| 103 | <div class="isd-stat"> |
| 104 | <span class="isd-stat-val{% if symbol_anchors %} isd-stat-val--accent{% endif %}">{{ symbol_anchors | length | fmtnum }}</span> |
| 105 | <span class="isd-stat-label">Anchors</span> |
| 106 | </div> |
| 107 | <div class="isd-stat"> |
| 108 | {%- if anchor_intel %} |
| 109 | <span class="isd-stat-val isd-stat-val--hot">{{ _blast_total.v | fmtnum }}</span> |
| 110 | {%- else %} |
| 111 | <span class="isd-stat-val isd-stat-val--dim">—</span> |
| 112 | {%- endif %} |
| 113 | <span class="isd-stat-label">Blast radius</span> |
| 114 | </div> |
| 115 | <div class="isd-stat"> |
| 116 | {%- if anchor_intel %} |
| 117 | <span class="isd-stat-val isd-stat-val--hot">{{ _churn30_total.v | fmtnum }}</span> |
| 118 | {%- else %} |
| 119 | <span class="isd-stat-val isd-stat-val--dim">—</span> |
| 120 | {%- endif %} |
| 121 | <span class="isd-stat-label">Churn 30d</span> |
| 122 | </div> |
| 123 | <div class="isd-stat"> |
| 124 | <span class="isd-stat-val{% if linked_proposals %} isd-stat-val--accent{% endif %}">{{ linked_proposals | length | fmtnum }}</span> |
| 125 | <span class="isd-stat-label">Proposals</span> |
| 126 | </div> |
| 127 | </div> |
| 128 | </div> |
| 129 | |
| 130 | {# ── Body ──────────────────────────────────────────────────────────────────── #} |
| 131 | <div class="isd-wrap"> |
| 132 | <div class="isd-layout"> |
| 133 | |
| 134 | {# ═══ Main column (65%) ══════════════════════════════════════════════════ #} |
| 135 | {# |
| 136 | Card order — designed to guide a developer from the familiar to the new: |
| 137 | 1. Description — what the issue is (familiar) |
| 138 | 2. Activity — the conversation thread (familiar) |
| 139 | 3. Commits — linked commits (bridge: devs know commits) |
| 140 | 4. Symbols — pinned functions / classes (new Muse concept) |
| 141 | 5. Intelligence — blast radius, churn, gravity (full Muse payoff) |
| 142 | #} |
| 143 | <div class="isd-main"> |
| 144 | |
| 145 | {# 1. Issue description — what is this issue? #} |
| 146 | <div class="isd-body-card"> |
| 147 | <div class="isd-body-bd"> |
| 148 | {%- if issue.body %} |
| 149 | <div class="isd-body-content">{{ issue.body | markdown | safe }}</div> |
| 150 | {%- else %} |
| 151 | <div class="isd-body-empty">No description provided.</div> |
| 152 | {%- endif %} |
| 153 | </div> |
| 154 | </div> |
| 155 | |
| 156 | {# 2. Activity timeline — the conversation (familiar from any issue tracker) #} |
| 157 | <div class="isd-timeline"> |
| 158 | <div class="isd-timeline-hd"> |
| 159 | <span class="isd-timeline-icon">◎</span> |
| 160 | <span class="isd-timeline-title">Activity</span> |
| 161 | {%- set comment_count = timeline | selectattr('kind', 'equalto', 'comment') | list | length %} |
| 162 | {%- if comment_count %}<span class="isd-panel-count">{{ comment_count | fmtnum }}</span>{%- endif %} |
| 163 | </div> |
| 164 | <div id="issue-comments" class="isd-timeline-bd"> |
| 165 | {% include "musehub/fragments/issue_comments.html" %} |
| 166 | </div> |
| 167 | </div> |
| 168 | |
| 169 | {# 3. Symbol anchors — Muse-native: pin the exact functions / classes in scope #} |
| 170 | {%- if symbol_anchors %} |
| 171 | <div class="isd-panel isd-panel--anchors"> |
| 172 | <div class="isd-panel-hd"> |
| 173 | <span class="isd-panel-icon">◈</span> |
| 174 | <span class="isd-panel-title">Symbol Anchors</span> |
| 175 | <span class="isd-panel-count">{{ symbol_anchors | length | fmtnum }}</span> |
| 176 | </div> |
| 177 | <div class="isd-panel-bd isd-panel-bd--flush"> |
| 178 | {%- for anchor in symbol_anchors %} |
| 179 | {%- set ar = parse_symbol_anchor(anchor) %} |
| 180 | {%- set site = site_base_url() %} |
| 181 | {%- set blob_href = ar.blob_url(site, owner, repo_slug) %} |
| 182 | {%- set ai = anchor_intel.get(anchor, {}) %} |
| 183 | <a href="{{ blob_href }}" class="isd-anchor{% if ar.is_cross_repo %} isd-anchor--cross-repo{% endif %}"> |
| 184 | <span class="isd-anchor-addr"> |
| 185 | {%- if ar.is_cross_repo %}<span class="isd-anchor-repo">{{ ar.owner }}/{{ ar.repo }}</span><span class="isd-anchor-sep">::</span>{%- endif %} |
| 186 | <span class="isd-anchor-file">{{ ar.file_path }}</span> |
| 187 | {%- if ar.symbol_name %}<span class="isd-anchor-sep">::</span><span class="isd-anchor-sym">{{ ar.symbol_name }}</span>{%- endif %} |
| 188 | {%- if ar.ref %}<span class="isd-anchor-ref">@{{ ar.ref }}</span>{%- endif %} |
| 189 | </span> |
| 190 | <span class="isd-anchor-stats"> |
| 191 | {%- set blast = ai.blast if ai.blast is defined and ai.blast is not none else none %} |
| 192 | {%- set churn = ai.churn_30d if ai.churn_30d is defined and ai.churn_30d is not none else none %} |
| 193 | {%- if blast is not none %}<span class="isd-anchor-stat" title="Blast radius">⬡ {{ blast | fmtnum }}</span>{%- endif %} |
| 194 | {%- if churn is not none %}<span class="isd-anchor-stat" title="Churn last 30d">↻ {{ churn | fmtnum }}</span>{%- endif %} |
| 195 | {%- if ai.last_author is defined and ai.last_author %}<span class="isd-anchor-author">{{ ai.last_author }}</span>{%- endif %} |
| 196 | </span> |
| 197 | <span class="isd-anchor-arrow">↗</span> |
| 198 | </a> |
| 199 | {%- endfor %} |
| 200 | </div> |
| 201 | </div> |
| 202 | {%- endif %} |
| 203 | |
| 204 | {# 4. Intelligence — Muse singularity mode: blast radius, churn velocity, gravity, sparklines #} |
| 205 | {%- set _first_ai = namespace(v=none) %} |
| 206 | {%- for anchor in symbol_anchors %} |
| 207 | {%- set _ai = anchor_intel.get(anchor, {}) if anchor_intel else {} %} |
| 208 | {%- if _ai and _first_ai.v is none %}{%- set _first_ai.v = _ai %}{%- endif %} |
| 209 | {%- endfor %} |
| 210 | {%- if _first_ai.v %} |
| 211 | {%- set ai = _first_ai.v %} |
| 212 | {%- set grav_raw = ai.gravity if ai.gravity is defined else 0 %} |
| 213 | {%- set grav_pct = (grav_raw * 100) | round(2) %} |
| 214 | {%- set grav_cls = 'high' if grav_pct >= 5 else ('mid' if grav_pct >= 1 else 'low') %} |
| 215 | {%- set blast_val = ai.blast if ai.blast is defined and ai.blast is not none else 0 %} |
| 216 | {%- set bd = ai.blast_direct if ai.blast_direct is defined else 0 %} |
| 217 | {%- set bc = ai.blast_cross if ai.blast_cross is defined else 0 %} |
| 218 | {%- set churn_val = ai.churn_90d if ai.churn_90d is defined else (ai.churn if ai.churn is defined else 0) %} |
| 219 | {%- set author_val = ai.author_count if ai.author_count is defined else 0 %} |
| 220 | <div class="isd-panel isd-panel--intel"> |
| 221 | <div class="isd-panel-hd"> |
| 222 | <span class="isd-panel-icon">⬡</span> |
| 223 | <span class="isd-panel-title">Intelligence</span> |
| 224 | </div> |
| 225 | <div class="isd-panel-bd"> |
| 226 | |
| 227 | {# ── Blast radius ── #} |
| 228 | <div class="isd-ic-section isd-ic-section--blast"> |
| 229 | <div class="isd-ic-section-label">Blast radius</div> |
| 230 | <div class="isd-ic-blast-nums"> |
| 231 | <span class="isd-ic-blast-num isd-ic-blast-num--direct">{{ bd | fmtnum }}</span> |
| 232 | <span class="isd-ic-blast-sep">direct dependents</span> |
| 233 | <span class="isd-ic-blast-dot">·</span> |
| 234 | <span class="isd-ic-blast-num isd-ic-blast-num--cross">{{ bc | fmtnum }}</span> |
| 235 | <span class="isd-ic-blast-sep">transitive</span> |
| 236 | </div> |
| 237 | <div class="isd-ic-grav-wrap"> |
| 238 | <div class="isd-ic-grav-track"> |
| 239 | <div class="isd-ic-grav-fill isd-ic-grav-fill--{{ grav_cls }}" style="width:{{ [grav_pct, 100] | min }}%"></div> |
| 240 | </div> |
| 241 | <span class="isd-ic-grav-label isd-ic-grav-label--{{ grav_cls }}">gravity: {{ grav_pct }}% of codebase</span> |
| 242 | </div> |
| 243 | {%- if ai.blast_top is defined and ai.blast_top %} |
| 244 | <div class="isd-ic-co-syms"> |
| 245 | <span class="isd-ic-co-label">top callers:</span> |
| 246 | {%- for co_path in ai.blast_top %} |
| 247 | <span class="isd-ic-co-sym" title="{{ co_path }}">{{ co_path.split('/')[-1] }}</span> |
| 248 | {%- endfor %} |
| 249 | {%- if blast_val > (ai.blast_top | length) %} |
| 250 | <span class="isd-ic-co-sym isd-ic-co-sym--more">+{{ (blast_val - (ai.blast_top | length)) | fmtnum }}</span> |
| 251 | {%- endif %} |
| 252 | </div> |
| 253 | {%- endif %} |
| 254 | </div> |
| 255 | |
| 256 | {# ── Churn ── #} |
| 257 | <div class="isd-ic-section isd-ic-section--churn"> |
| 258 | <div class="isd-ic-section-label">Churn (last 90 days)</div> |
| 259 | <div class="isd-ic-churn-line"> |
| 260 | <strong class="isd-ic-churn-n">{{ churn_val | fmtnum }}</strong> |
| 261 | <span class="isd-ic-churn-txt">modification{{ 's' if churn_val != 1 }}</span> |
| 262 | {%- if author_val > 0 %} |
| 263 | <span class="isd-ic-churn-dot">·</span> |
| 264 | <strong class="isd-ic-churn-n">{{ author_val | fmtnum }}</strong> |
| 265 | <span class="isd-ic-churn-txt">author{{ 's' if author_val != 1 }}</span> |
| 266 | {%- endif %} |
| 267 | {%- if ai.last_changed is defined and ai.last_changed %} |
| 268 | <span class="isd-ic-churn-dot">·</span> |
| 269 | <span class="isd-ic-churn-txt">last touched</span> |
| 270 | <span class="isd-ic-churn-ts">{{ ai.last_changed | fmtdate }}</span> |
| 271 | {%- if ai.last_author is defined and ai.last_author %} |
| 272 | <strong class="isd-ic-churn-author">{{ ai.last_author }}</strong> |
| 273 | {%- endif %} |
| 274 | {%- endif %} |
| 275 | </div> |
| 276 | {%- if ai.weekly is defined and ai.weekly %} |
| 277 | {%- set _sm = namespace(v=1) %} |
| 278 | {%- for w in ai.weekly %}{%- if w > _sm.v %}{%- set _sm.v = w %}{%- endif %}{%- endfor %} |
| 279 | <div class="isd-ic-sparkline" title="Weekly changes — 12 weeks, newest on left"> |
| 280 | {%- for w in ai.weekly %} |
| 281 | {%- if w > 0 %} |
| 282 | <span class="isd-ic-spark-bar" |
| 283 | style="height:{{ [((w / _sm.v) * 44) | round | int, 4] | max }}px" |
| 284 | title="Week -{{ loop.index0 }}: {{ w }} change{{ 's' if w != 1 }}"></span> |
| 285 | {%- else %} |
| 286 | <span class="isd-ic-spark-gap"></span> |
| 287 | {%- endif %} |
| 288 | {%- endfor %} |
| 289 | </div> |
| 290 | {%- endif %} |
| 291 | </div> |
| 292 | |
| 293 | </div> |
| 294 | </div> |
| 295 | {%- endif %} |
| 296 | |
| 297 | </div>{# /isd-main #} |
| 298 | |
| 299 | {# ═══ Sidebar (35%) ══════════════════════════════════════════════════════ #} |
| 300 | <aside class="isd-sidebar"> |
| 301 | |
| 302 | {# Assignee #} |
| 303 | <div class="isd-sb-card"> |
| 304 | <div class="isd-sb-hd">Assignee</div> |
| 305 | <div class="isd-sb-bd"> |
| 306 | {%- if issue.assignee %} |
| 307 | <div class="isd-assignee"> |
| 308 | <img src="/avatars/handle/{{ issue.assignee | urlencode }}.svg" class="isd-assignee-sigil" width="32" height="32" alt="" aria-hidden="true"> |
| 309 | <div class="isd-assignee-info"> |
| 310 | <a href="/{{ issue.assignee }}" class="isd-actor-link">{{ issue.assignee }}</a> |
| 311 | <span class="isd-identity-badge isd-identity-badge--{{ assignee_identity_type }}">{{ assignee_identity_type }}</span> |
| 312 | </div> |
| 313 | </div> |
| 314 | {%- else %} |
| 315 | <span class="isd-muted">○ unassigned</span> |
| 316 | {%- endif %} |
| 317 | </div> |
| 318 | </div> |
| 319 | |
| 320 | {# Linked proposals — Signals 1, 2, 3: commit graph, symbol overlap, branch reachability #} |
| 321 | {%- if linked_proposals %} |
| 322 | {%- set _reasons = linked_proposals | map(attribute='match_reason') | list | unique | sort | list %} |
| 323 | {%- if _reasons == ["commit_graph"] %} |
| 324 | {%- set _label = "via commit graph" %} |
| 325 | {%- set _tip = "Matched by VCS commit graph ancestry (merged)" %} |
| 326 | {%- elif _reasons == ["branch_reachability"] %} |
| 327 | {%- set _label = "in-flight" %} |
| 328 | {%- set _tip = "Commit reachable from from_branch but not yet in to_branch" %} |
| 329 | {%- elif _reasons == ["symbol_overlap"] %} |
| 330 | {%- set _label = "via symbol overlap" %} |
| 331 | {%- set _tip = "Matched by symbol address overlap" %} |
| 332 | {%- elif "commit_graph" in _reasons and "branch_reachability" in _reasons %} |
| 333 | {%- set _label = "via commit graph + in-flight" %} |
| 334 | {%- set _tip = "Some proposals merged (commit graph), others in-flight (branch reachability)" %} |
| 335 | {%- else %} |
| 336 | {%- set _label = "via Muse graph" %} |
| 337 | {%- set _tip = "Matched by VCS commit graph, branch reachability, or symbol overlap" %} |
| 338 | {%- endif %} |
| 339 | <div class="isd-sb-card"> |
| 340 | <div class="isd-sb-hd"> |
| 341 | Linked Proposals |
| 342 | <span class="isd-lp-signal-badge" title="{{ _tip }}">{{ _label }}</span> |
| 343 | </div> |
| 344 | <div class="isd-sb-bd isd-sb-bd--flush"> |
| 345 | {%- for lp in linked_proposals %} |
| 346 | <a href="{{ base_url }}/proposals/{{ lp.proposal_id }}" class="isd-sb-proposal-row"> |
| 347 | <span class="isd-lp-state isd-lp-state--{{ lp.state }}"> |
| 348 | {%- if lp.state == "merged" %}●{%- elif lp.state == "open" %}○{%- else %}✕{%- endif %} |
| 349 | </span> |
| 350 | <span class="isd-lp-ref">#{{ lp.proposal_number }}</span> |
| 351 | <span class="isd-lp-title">{{ lp.title | truncate(34, True, '…') }}</span> |
| 352 | {%- if lp.match_reason == "branch_reachability" %} |
| 353 | <span class="isd-lp-sym-dot isd-lp-sym-dot--inflight" title="In-flight: commit reachable from branch, not yet merged">⬆</span> |
| 354 | {%- elif lp.match_reason == "symbol_overlap" %} |
| 355 | <span class="isd-lp-sym-dot" title="Matched via symbol overlap">◆</span> |
| 356 | {%- endif %} |
| 357 | </a> |
| 358 | {%- endfor %} |
| 359 | </div> |
| 360 | </div> |
| 361 | {%- endif %} |
| 362 | |
| 363 | {# Release — Muse-native VCS graph release tracking #} |
| 364 | <div class="isd-sb-card isd-sb-card--release"> |
| 365 | <div class="isd-sb-hd"> |
| 366 | Release |
| 367 | {%- if release_context.all_landed %}<span class="isd-rel-status-chip isd-rel-status-chip--landed">landed</span> |
| 368 | {%- elif release_context.any_landed %}<span class="isd-rel-status-chip isd-rel-status-chip--partial">partial</span> |
| 369 | {%- elif not release_context.no_commits %}<span class="isd-rel-status-chip isd-rel-status-chip--pending">unreleased</span> |
| 370 | {%- endif %} |
| 371 | </div> |
| 372 | <div class="isd-sb-bd isd-release-bd"> |
| 373 | {%- if release_context.no_commits %} |
| 374 | <span class="isd-release-none">no commits linked to this issue</span> |
| 375 | {%- else %} |
| 376 | {%- for ac in release_context.anchor_commits %} |
| 377 | <div class="isd-rel-commit {% if ac.landed_in %}isd-rel-commit--landed{% else %}isd-rel-commit--pending{% endif %}"> |
| 378 | <div class="isd-rel-commit-top"> |
| 379 | <a href="{{ base_url }}/commits/{{ ac.full }}" class="isd-rel-hash">{{ ac.short }}</a> |
| 380 | {%- if ac.landed_in %} |
| 381 | <span class="isd-rel-landed-in"> |
| 382 | {%- for rel in ac.landed_in %} |
| 383 | <a href="{{ base_url }}/releases/{{ rel.tag }}" class="isd-rel-ver isd-rel-ver--landed">{{ rel.tag }}</a> |
| 384 | {%- endfor %} |
| 385 | </span> |
| 386 | {%- else %} |
| 387 | <span class="isd-rel-on-branch">{{ ac.branch or 'dev' }}</span> |
| 388 | {%- endif %} |
| 389 | </div> |
| 390 | {%- if ac.message %} |
| 391 | <div class="isd-rel-msg">"{{ ac.message }}"</div> |
| 392 | {%- endif %} |
| 393 | {%- if ac.author or ac.date %} |
| 394 | <div class="isd-rel-meta"> |
| 395 | {%- if ac.author %}<span class="isd-rel-author">{{ ac.author }}</span>{%- endif %} |
| 396 | {%- if ac.author and ac.date %}<span class="isd-rel-sep">·</span>{%- endif %} |
| 397 | {%- if ac.date %}<span class="isd-rel-date">{{ ac.date | fmtdate }}</span>{%- endif %} |
| 398 | </div> |
| 399 | {%- endif %} |
| 400 | </div> |
| 401 | {%- endfor %} |
| 402 | {%- if not release_context.all_landed and release_context.next_tag %} |
| 403 | <div class="isd-rel-next"> |
| 404 | <span class="isd-rel-next-label">next release</span> |
| 405 | <span class="isd-rel-next-tag">{{ release_context.next_tag }}</span> |
| 406 | <span class="isd-rel-next-hint">proposed</span> |
| 407 | </div> |
| 408 | {%- endif %} |
| 409 | {%- endif %} |
| 410 | </div> |
| 411 | </div> |
| 412 | |
| 413 | {# Act via CLI / MCP / REST — tabbed #} |
| 414 | <div class="isd-sb-card isd-sb-card--cli" x-data="tabSwitcher"> |
| 415 | <div class="isd-sb-hd isd-sb-hd--tabs"> |
| 416 | <button class="isd-act-tab" :class="cliClass" @click="setCliTab">CLI</button> |
| 417 | <button class="isd-act-tab" :class="mcpClass" @click="setMcpTab">MCP</button> |
| 418 | <button class="isd-act-tab" :class="restClass" @click="setRestTab">REST</button> |
| 419 | </div> |
| 420 | |
| 421 | {# ── CLI pane ───────────────────────────────────────────────────────── #} |
| 422 | <div class="isd-sb-bd" x-show="showCli"> |
| 423 | |
| 424 | <div class="isd-cli-label">create</div> |
| 425 | <div class="isd-snippet-wrap"> |
| 426 | <pre class="isd-cli-snippet"><code class="language-bash">muse hub issue create \ |
| 427 | --title "..." \ |
| 428 | --body "..." \ |
| 429 | --label bug \ |
| 430 | --anchor path/to/file.py::Symbol \ |
| 431 | --commit-anchor <sha> \ |
| 432 | --repo {{ owner }}/{{ repo_slug }}</code></pre> |
| 433 | </div> |
| 434 | |
| 435 | <div class="isd-cli-label">read</div> |
| 436 | <div class="isd-snippet-wrap"> |
| 437 | <pre class="isd-cli-snippet"><code class="language-bash">muse hub issue get {{ issue.number }} --json |
| 438 | muse hub issue list --state open --json</code></pre> |
| 439 | </div> |
| 440 | |
| 441 | <div class="isd-cli-label">update</div> |
| 442 | <div class="isd-snippet-wrap"> |
| 443 | <pre class="isd-cli-snippet"><code class="language-bash">muse hub issue edit {{ issue.number }} \ |
| 444 | {%- if issue.symbol_anchors %} |
| 445 | {%- for anchor in issue.symbol_anchors %} |
| 446 | --anchor {{ anchor }} \ |
| 447 | {%- endfor %} |
| 448 | {%- else %} |
| 449 | --anchor path/to/file.py::Symbol \ |
| 450 | {%- endif %} |
| 451 | {%- if issue.commit_anchors %} |
| 452 | {%- for ca in issue.commit_anchors %} |
| 453 | --commit-anchor {{ ca }} \ |
| 454 | {%- endfor %} |
| 455 | {%- endif %} |
| 456 | --repo {{ owner }}/{{ repo_slug }}</code></pre> |
| 457 | </div> |
| 458 | |
| 459 | <div class="isd-cli-label">comment</div> |
| 460 | <div class="isd-snippet-wrap"> |
| 461 | <pre class="isd-cli-snippet"><code class="language-bash">muse hub issue comment {{ issue.number }} \ |
| 462 | --body "Fixed in <sha>" \ |
| 463 | --repo {{ owner }}/{{ repo_slug }}</code></pre> |
| 464 | </div> |
| 465 | |
| 466 | <div class="isd-cli-label">{% if issue.state == 'open' %}close{% else %}reopen{% endif %}</div> |
| 467 | <div class="isd-snippet-wrap"> |
| 468 | <pre class="isd-cli-snippet"><code class="language-bash">muse hub issue {% if issue.state == 'open' %}close{% else %}reopen{% endif %} {{ issue.number }} \ |
| 469 | --repo {{ owner }}/{{ repo_slug }}</code></pre> |
| 470 | </div> |
| 471 | </div> |
| 472 | |
| 473 | {# ── MCP pane ───────────────────────────────────────────────────────── #} |
| 474 | <div class="isd-sb-bd" x-show="showMcp" x-cloak> |
| 475 | |
| 476 | <div class="isd-cli-label">create</div> |
| 477 | <div class="isd-snippet-wrap"> |
| 478 | <pre class="isd-cli-snippet"><code class="language-javascript">create_issue({ |
| 479 | repo_id: "{{ repo_id }}", |
| 480 | title: "...", |
| 481 | body: "...", |
| 482 | labels: ["bug"], |
| 483 | symbol_anchors: [ |
| 484 | "path/to/file.py::Symbol" |
| 485 | ], |
| 486 | commit_anchors: ["<sha>"] |
| 487 | })</code></pre> |
| 488 | </div> |
| 489 | |
| 490 | <div class="isd-cli-label">read</div> |
| 491 | <div class="isd-snippet-wrap"> |
| 492 | <pre class="isd-cli-snippet"><code class="language-javascript">get_issue({ |
| 493 | repo_id: "{{ repo_id }}", |
| 494 | issue_number: {{ issue.number }} |
| 495 | }) |
| 496 | |
| 497 | list_issues({ |
| 498 | repo_id: "{{ repo_id }}", |
| 499 | state: "open" |
| 500 | })</code></pre> |
| 501 | </div> |
| 502 | |
| 503 | <div class="isd-cli-label">update</div> |
| 504 | <div class="isd-snippet-wrap"> |
| 505 | <pre class="isd-cli-snippet"><code class="language-javascript">edit_issue({ |
| 506 | repo_id: "{{ repo_id }}", |
| 507 | issue_number: {{ issue.number }}, |
| 508 | {%- if issue.symbol_anchors %} |
| 509 | symbol_anchors: [ |
| 510 | {%- for anchor in issue.symbol_anchors %} |
| 511 | "{{ anchor }}"{{ "," if not loop.last }} |
| 512 | {%- endfor %} |
| 513 | ] |
| 514 | {%- else %} |
| 515 | symbol_anchors: ["path/to/file.py::Symbol"] |
| 516 | {%- endif %} |
| 517 | })</code></pre> |
| 518 | </div> |
| 519 | |
| 520 | <div class="isd-cli-label">comment</div> |
| 521 | <div class="isd-snippet-wrap"> |
| 522 | <pre class="isd-cli-snippet"><code class="language-javascript">create_issue_comment({ |
| 523 | repo_id: "{{ repo_id }}", |
| 524 | issue_number: {{ issue.number }}, |
| 525 | body: "..." |
| 526 | })</code></pre> |
| 527 | </div> |
| 528 | |
| 529 | <div class="isd-cli-label">{% if issue.state == 'open' %}close{% else %}reopen{% endif %}</div> |
| 530 | <div class="isd-snippet-wrap"> |
| 531 | <pre class="isd-cli-snippet"><code class="language-javascript">{% if issue.state == 'open' %}close{% else %}reopen{% endif %}_issue({ |
| 532 | repo_id: "{{ repo_id }}", |
| 533 | issue_number: {{ issue.number }} |
| 534 | })</code></pre> |
| 535 | </div> |
| 536 | </div> |
| 537 | |
| 538 | {# ── REST pane ──────────────────────────────────────────────────────── #} |
| 539 | <div class="isd-sb-bd" x-show="showRest" x-cloak> |
| 540 | |
| 541 | <div class="isd-cli-label">create</div> |
| 542 | <div class="isd-snippet-wrap"> |
| 543 | <pre class="isd-cli-snippet"><code class="language-bash">curl -X POST \ |
| 544 | http://localhost:10003/api/repos/{{ repo_id }}/issues \ |
| 545 | -H "Content-Type: application/json" \ |
| 546 | -H "Authorization: MSign handle=\"...\" ts=... sig=\"...\"" \ |
| 547 | -d '{ |
| 548 | "title": "...", |
| 549 | "body": "...", |
| 550 | "labels": ["bug"], |
| 551 | "symbol_anchors": ["path/to/file.py::Symbol"] |
| 552 | }'</code></pre> |
| 553 | </div> |
| 554 | |
| 555 | <div class="isd-cli-label">read</div> |
| 556 | <div class="isd-snippet-wrap"> |
| 557 | <pre class="isd-cli-snippet"><code class="language-bash"># get one issue |
| 558 | curl http://localhost:10003/api/repos/{{ repo_id }}/issues/{{ issue.number }} |
| 559 | |
| 560 | # list open issues |
| 561 | curl "http://localhost:10003/api/repos/{{ repo_id }}/issues?state=open"</code></pre> |
| 562 | </div> |
| 563 | |
| 564 | <div class="isd-cli-label">update</div> |
| 565 | <div class="isd-snippet-wrap"> |
| 566 | <pre class="isd-cli-snippet"><code class="language-bash">curl -X PATCH \ |
| 567 | http://localhost:10003/api/repos/{{ repo_id }}/issues/{{ issue.number }} \ |
| 568 | -H "Content-Type: application/json" \ |
| 569 | -H "Authorization: MSign handle=\"...\" ts=... sig=\"...\"" \ |
| 570 | -d '{"title": "...", "body": "..."}'</code></pre> |
| 571 | </div> |
| 572 | |
| 573 | <div class="isd-cli-label">comment</div> |
| 574 | <div class="isd-snippet-wrap"> |
| 575 | <pre class="isd-cli-snippet"><code class="language-bash">curl -X POST \ |
| 576 | http://localhost:10003/api/repos/{{ repo_id }}/issues/{{ issue.number }}/comments \ |
| 577 | -H "Content-Type: application/json" \ |
| 578 | -H "Authorization: MSign handle=\"...\" ts=... sig=\"...\"" \ |
| 579 | -d '{"body": "Fixed in <sha>"}'</code></pre> |
| 580 | </div> |
| 581 | |
| 582 | <div class="isd-cli-label">{% if issue.state == 'open' %}close{% else %}reopen{% endif %}</div> |
| 583 | <div class="isd-snippet-wrap"> |
| 584 | <pre class="isd-cli-snippet"><code class="language-bash">curl -X POST \ |
| 585 | http://localhost:10003/api/repos/{{ repo_id }}/issues/{{ issue.number }}/{% if issue.state == 'open' %}close{% else %}reopen{% endif %} \ |
| 586 | -H "Authorization: MSign handle=\"...\" ts=... sig=\"...\""</code></pre> |
| 587 | </div> |
| 588 | </div> |
| 589 | </div> |
| 590 | |
| 591 | </aside> |
| 592 | |
| 593 | </div> |
| 594 | </div> |
| 595 | |
| 596 | </div> |
| 597 | {% endblock %} |
File History
2 commits
sha256:f99af7b1a7f36c4d537d1c630d4b71fc39222b1255f82e930929e2fc89015e11
fix: relax browse_repo perf budget to 500ms — 200ms was too…
Sonnet 4.6
99 days ago
sha256:763eb2cb8675073b84c19345b27586d2ed939a9aee97c5479b69f502f1a70eff
fix(tests): update test suite to match current implementation
Sonnet 4.6
patch
121 days ago