Skip to content

Commit 7e48f17

Browse files
fix(summaries): friendly empty state when summaries.json is missing
Previously a workspace without summaries.json showed a red error banner 'File not found: summaries.json'. The 404 is the expected state on a fresh workspace — polish hasn't run yet — not an error. Now detects the 404 specifically and renders a centered empty-state card with the exact command to generate the file (attune-author polish) plus a one-paragraph explanation of what the file does. Other failure modes (corrupt JSON, real I/O errors) still surface the danger banner. Adds .empty-state and .cmd-snippet styles in static_cw/style.css. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 834bc29 commit 7e48f17

3 files changed

Lines changed: 94 additions & 34 deletions

File tree

sidecar/attune_gui/routes/cowork_pages.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ async def page_summaries(request: Request) -> HTMLResponse:
163163

164164
summaries: dict[str, str] = {}
165165
error: str | None = None
166+
not_yet_generated = False
167+
166168
try:
167169
data = await cowork_files.read_file(root="summaries", path="summaries.json")
168170
try:
@@ -173,10 +175,23 @@ async def page_summaries(request: Request) -> HTMLResponse:
173175
except json.JSONDecodeError as exc:
174176
error = f"Invalid JSON in summaries.json: {exc}"
175177
except HTTPException as exc:
176-
error = exc.detail if isinstance(exc.detail, str) else "Could not load summaries.json"
178+
# 404 = file simply hasn't been generated yet → friendly empty state
179+
# rather than a red error banner.
180+
if exc.status_code == 404:
181+
not_yet_generated = True
182+
else:
183+
error = exc.detail if isinstance(exc.detail, str) else "Could not load summaries.json"
177184

178185
return templates.TemplateResponse(
179-
request, "summaries.html", _ctx(request, "summaries", summaries=summaries, error=error)
186+
request,
187+
"summaries.html",
188+
_ctx(
189+
request,
190+
"summaries",
191+
summaries=summaries,
192+
error=error,
193+
not_yet_generated=not_yet_generated,
194+
),
180195
)
181196

182197

sidecar/attune_gui/static_cw/style.css

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,34 @@ code, .mono {
444444
padding: 10px 14px;
445445
}
446446

447+
/* Friendly empty states (page-level, not inline) */
448+
.empty-state {
449+
text-align: center;
450+
padding: 36px 28px;
451+
max-width: 520px;
452+
margin: 32px auto;
453+
}
454+
.empty-state-title {
455+
margin: 0 0 8px;
456+
font-size: 1.1rem;
457+
font-weight: 600;
458+
letter-spacing: -0.01em;
459+
}
460+
.empty-state-body {
461+
color: var(--ink-soft);
462+
margin: 0 0 14px;
463+
}
464+
.cmd-snippet {
465+
display: inline-block;
466+
background: var(--ink);
467+
color: #f5f5f7;
468+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
469+
font-size: 0.85rem;
470+
padding: 8px 14px;
471+
border-radius: var(--radius-sm);
472+
margin: 0;
473+
}
474+
447475
/* ── Banners ───────────────────────────────────────────────────────────── */
448476

449477
.banner {

sidecar/attune_gui/templates/summaries.html

Lines changed: 49 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,57 @@
33
{% block title %}Summaries{% endblock %}
44

55
{% block content %}
6-
<p class="banner warn">
7-
<code>summaries.json</code> is fully regenerated by <code>attune-author polish</code>.
8-
Manual edits will be overwritten on the next run.
9-
</p>
6+
{% if not_yet_generated %}
7+
<div class="card empty-state">
8+
<h2 class="empty-state-title">No summaries yet</h2>
9+
<p class="empty-state-body">
10+
<code class="mono">summaries.json</code> hasn't been generated for this
11+
workspace yet. Run the polish pass to create it:
12+
</p>
13+
<pre class="cmd-snippet">attune-author polish</pre>
14+
<p class="dim small" style="margin-top:1rem;">
15+
Once generated, this page will show every entry in a table you can
16+
edit inline. The polish command runs an LLM pass over your templates
17+
to produce path-keyed summaries that the RAG retriever uses for boost
18+
scoring.
19+
</p>
20+
</div>
21+
{% else %}
22+
<p class="banner warn">
23+
<code>summaries.json</code> is fully regenerated by <code>attune-author polish</code>.
24+
Manual edits will be overwritten on the next run.
25+
</p>
1026

11-
{% if error %}
12-
<p class="banner danger">{{ error }}</p>
13-
{% endif %}
27+
{% if error %}
28+
<p class="banner danger">{{ error }}</p>
29+
{% endif %}
1430

15-
{% if summaries %}
16-
<div class="table-wrap">
17-
<table class="data-table">
18-
<thead>
19-
<tr>
20-
<th style="width: 38%;">Path</th>
21-
<th>Summary</th>
22-
<th style="width: 7rem;"></th>
23-
</tr>
24-
</thead>
25-
<tbody>
26-
{% for path, summary in summaries.items() %}
27-
<tr>
28-
<td><code class="mono">{{ path }}</code></td>
29-
<td>
30-
<textarea class="inline-edit" data-key="{{ path }}" rows="2">{{ summary }}</textarea>
31-
</td>
32-
<td><button class="btn ghost btn-save" data-key="{{ path }}">Save</button></td>
33-
</tr>
34-
{% endfor %}
35-
</tbody>
36-
</table>
37-
</div>
38-
{% elif not error %}
39-
<p class="empty">No summaries yet. Run <code>attune-author polish</code> to generate them.</p>
31+
{% if summaries %}
32+
<div class="table-wrap">
33+
<table class="data-table">
34+
<thead>
35+
<tr>
36+
<th style="width: 38%;">Path</th>
37+
<th>Summary</th>
38+
<th style="width: 7rem;"></th>
39+
</tr>
40+
</thead>
41+
<tbody>
42+
{% for path, summary in summaries.items() %}
43+
<tr>
44+
<td><code class="mono">{{ path }}</code></td>
45+
<td>
46+
<textarea class="inline-edit" data-key="{{ path }}" rows="2">{{ summary }}</textarea>
47+
</td>
48+
<td><button class="btn ghost btn-save" data-key="{{ path }}">Save</button></td>
49+
</tr>
50+
{% endfor %}
51+
</tbody>
52+
</table>
53+
</div>
54+
{% elif not error %}
55+
<p class="empty">summaries.json is empty.</p>
56+
{% endif %}
4057
{% endif %}
4158
{% endblock %}
4259

0 commit comments

Comments
 (0)