Keep the home memo form when the decision overview fails

The overview is orientation only; a store, policy or engine failure while
listing now shows a notice instead of an error page, so the memo form and
sign-out stay reachable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 359683@bnt-lap001
Assistant-Session: eebdc939-7a9b-4e50-9d39-c8437e8a14ec
This commit is contained in:
tegwick 2026-09-21 22:12:27 +02:00
parent 002a486ecf
commit 3e0d3d5ebf
2 changed files with 22 additions and 1 deletions

View file

@ -188,7 +188,7 @@ class App:
if method == "GET" and path == "/": if method == "GET" and path == "/":
if session: if session:
content = (f"<p>Signed in as {html.escape(session.subject)}.</p>" content = (f"<p>Signed in as {html.escape(session.subject)}.</p>"
+ (overview_section(self.review.overview(session)) if self.review is not None else '') + (self._overview(session) if self.review is not None else '')
+ ('<h2>Open a decision review</h2><p>Enter the memo identifier supplied with your review request.</p>' + ('<h2>Open a decision review</h2><p>Enter the memo identifier supplied with your review request.</p>'
'<form method="get" action="/review"><label>Memo identifier <input name="memo_id" required maxlength="256"></label>' '<form method="get" action="/review"><label>Memo identifier <input name="memo_id" required maxlength="256"></label>'
'<button type="submit">Open review</button></form>' if self.review is not None else '<button type="submit">Open review</button></form>' if self.review is not None else
@ -201,6 +201,19 @@ class App:
return 200, document("Informed Decision", content, session.subject if session else None), "text/html", [] return 200, document("Informed Decision", content, session.subject if session else None), "text/html", []
return 404, "Not found.", "text/plain", [] return 404, "Not found.", "text/plain", []
def _overview(self, session):
# The overview is orientation only. Its failure must never take the
# memo form, sign-out or an unresolved-entry recovery path with it.
try:
return overview_section(self.review.overview(session))
except ReviewError as exc:
if exc.code == "session_expired":
raise
except (PolicyError, ApprovalEngineError, StoreError, sqlite3.Error, ValueError, KeyError, TypeError):
pass
return ('<h1>Your decisions</h1><div class="notice warning" role="status">Your decisions could not be '
'listed right now. You can still open a review by its memo identifier.</div>')
def _form(self, environ, session): def _form(self, environ, session):
if environ.get("HTTP_ORIGIN") != ORIGIN: if environ.get("HTTP_ORIGIN") != ORIGIN:
raise ReviewError(403, "invalid_form") raise ReviewError(403, "invalid_form")

View file

@ -407,3 +407,11 @@ def test_home_without_session_shows_sign_in_only(review):
r=call(app,'/') r=call(app,'/')
assert 'Sign in with KeyCape' in r['body'] and 'Your decisions' not in r['body'] assert 'Sign in with KeyCape' in r['body'] and 'Your decisions' not in r['body']
assert controller.store.policy_observations()==[] assert controller.store.policy_observations()==[]
def test_home_keeps_the_memo_form_when_the_overview_fails(review,monkeypatch):
controller,session,app,*_=review
def broken(subject):raise sqlite3.OperationalError('disk')
monkeypatch.setattr(controller.store,'memos_for',broken)
r=home(review)
assert r['status']==200 and 'could not be listed right now' in r['body'] and 'Open a decision review' in r['body']