From 212f6408007c8f705e733a4bb8a1aea47e967602 Mon Sep 17 00:00:00 2001 From: readwrightexecute Date: Wed, 14 Oct 2026 15:28:23 +0000 Subject: [PATCH 1/2] Fix stale Incipit completion UI --- webapp/app/main.py | 4 +-- .../app/templates/partials/question_list.html | 2 ++ .../app/templates/partials/section_card.html | 6 ++++ webapp/tests/test_routes.py | 31 ++++++++++++++++++- 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/webapp/app/main.py b/webapp/app/main.py index f43d76e..3bb1433 100644 --- a/webapp/app/main.py +++ b/webapp/app/main.py @@ -372,7 +372,7 @@ async def questions_partial(request: Request, sid: str): s = state.get(sid) if s is None: return _render("expired.html", request) - return _render("partials/question_list.html", request, s=s) + return _render("partials/question_list.html", request, s=s, oob=True) @app.post("/sessions/{sid}/answers", response_class=HTMLResponse) @@ -401,7 +401,7 @@ async def section_partial(request: Request, sid: str, section_id: str): if s is None or sec is None: return HTMLResponse("
Unknown section
") return _render("partials/section_card.html", request, s=s, sec=sec, - examples=flow.REFINE_EXAMPLES) + examples=flow.REFINE_EXAMPLES, oob=True) @app.post("/sessions/{sid}/sections/{section_id}/retry", response_class=HTMLResponse) diff --git a/webapp/app/templates/partials/question_list.html b/webapp/app/templates/partials/question_list.html index 1b5c5d4..bf570c0 100644 --- a/webapp/app/templates/partials/question_list.html +++ b/webapp/app/templates/partials/question_list.html @@ -1,9 +1,11 @@ {% if s.error %} +{% if oob %}
{% endif %}

Generation failed: {{ s.error }}

Start over

{% elif s.phase == 'clarify' and s.qas %} +{% if oob %}
{% endif %} {# Ready: plain div, no auto-refresh attrs — typed answers must survive #}
diff --git a/webapp/app/templates/partials/section_card.html b/webapp/app/templates/partials/section_card.html index 75e44cb..19cf5cc 100644 --- a/webapp/app/templates/partials/section_card.html +++ b/webapp/app/templates/partials/section_card.html @@ -1,3 +1,9 @@ +{% if oob and not (s.sections | selectattr("status", "in", ["pending", "generating"]) | list) %} +
+
+ {% include "partials/step3_actions.html" %} +
+{% endif %} {% if sec.status in ('pending', 'generating') %} {# Waiting: refreshes itself via SSE pokes + 10s polling fallback #}
Date: Wed, 14 Oct 2026 16:13:59 +0000 Subject: [PATCH 2/2] Fix question poll OOB swap --- webapp/app/main.py | 2 +- webapp/app/templates/partials/question_list.html | 12 +++++++----- webapp/tests/test_routes.py | 7 +++++-- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/webapp/app/main.py b/webapp/app/main.py index 3bb1433..42bbeb7 100644 --- a/webapp/app/main.py +++ b/webapp/app/main.py @@ -372,7 +372,7 @@ async def questions_partial(request: Request, sid: str): s = state.get(sid) if s is None: return _render("expired.html", request) - return _render("partials/question_list.html", request, s=s, oob=True) + return _render("partials/question_list.html", request, s=s, status_oob=True) @app.post("/sessions/{sid}/answers", response_class=HTMLResponse) diff --git a/webapp/app/templates/partials/question_list.html b/webapp/app/templates/partials/question_list.html index bf570c0..4a6162a 100644 --- a/webapp/app/templates/partials/question_list.html +++ b/webapp/app/templates/partials/question_list.html @@ -1,13 +1,15 @@ +{% set status_oob = status_oob|default(oob|default(false)) %} +{% set questions_oob = questions_oob|default(oob|default(false)) %} {% if s.error %} -{% if oob %}
{% endif %} -
+{% if status_oob %}
{% endif %} +

Generation failed: {{ s.error }}

Start over

{% elif s.phase == 'clarify' and s.qas %} -{% if oob %}
{% endif %} +{% if status_oob %}
{% endif %} {# Ready: plain div, no auto-refresh attrs — typed answers must survive #} -
+

Answer what you can — anything you skip falls back to the stated assumption.

{% for qa in s.qas %} @@ -33,7 +35,7 @@
{% else %} {# Waiting: keeps refreshing itself via SSE pokes + 10s polling fallback #} -
diff --git a/webapp/tests/test_routes.py b/webapp/tests/test_routes.py index b29beb0..ad84725 100644 --- a/webapp/tests/test_routes.py +++ b/webapp/tests/test_routes.py @@ -49,14 +49,17 @@ def test_party_questions_tolerates_missing_fields(): def test_questions_poll_clears_stale_status_bar(): """If the browser missed the final SSE clear, the polling fallback should - clear the initial cold-start status when questions are ready.""" + clear the initial cold-start status when questions are ready without marking + the polling target itself as an out-of-band swap.""" s = state.create() s.phase = "clarify" s.qas = [QA(question="Q1", assumption="A1")] resp = asyncio.run(main.questions_partial(_FakeRequest(), s.id)) + body = resp.body.decode() - assert 'id="status-bar" hx-swap-oob="innerHTML"' in resp.body.decode() + assert 'id="status-bar" hx-swap-oob="innerHTML"' in body + assert 'id="questions" hx-swap-oob="true"' not in body def test_last_section_poll_clears_stale_status_bar():