From 2d6d33815448351faf6efdc1ef0aa7b81d8cdd7a Mon Sep 17 00:00:00 2001 From: tegwick Date: Mon, 21 Sep 2026 21:58:20 +0200 Subject: [PATCH] Propose INFD-WP-0003: my-decisions overview on the review home page Co-Authored-By: Claude Opus 5 Assistant: claude-code Assistant-Model: opus Assistant-Process: 359683@bnt-lap001 Assistant-Session: eebdc939-7a9b-4e50-9d39-c8437e8a14ec --- .../INFD-WP-0003-my-decisions-overview.md | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 workplans/INFD-WP-0003-my-decisions-overview.md diff --git a/workplans/INFD-WP-0003-my-decisions-overview.md b/workplans/INFD-WP-0003-my-decisions-overview.md new file mode 100644 index 0000000..e1b78c8 --- /dev/null +++ b/workplans/INFD-WP-0003-my-decisions-overview.md @@ -0,0 +1,115 @@ +--- +id: INFD-WP-0003 +type: workplan +title: "My decisions overview — pending memos and the outcome of past dispositions" +domain: infotech +repo: informed-decision +status: proposed +owner: claude +topic_slug: netkingdom +created: "2026-09-21" +updated: "2026-09-21" +related: + - INFD-WP-0001 + - INFD-WP-0002 +origin: demand +origin_ref: "founder request 2026-09-21: review decisions taken, whether they were accepted, and which are still open" +--- + +# My decisions overview + +Founder request 2026-09-21: in the informed-decision UI, see (a) the +decisions I have taken and whether they were accepted, and (b) the decisions +still open for me to take. + +Today the signed-in home page offers only a free-text memo-identifier box +(`informed_decision/web.py`, `GET /`). A person has to already know each memo +id, and nothing shows the outcome of a past disposition unless that exact +review is reopened. + +## Why this does not breach the Stage 1 boundaries + +- **Not an approval-engine inbox.** The list is built from memos that + informed-decision itself holds, addressed to the signed-in subject + (`memo.binding.principal.id`). approval-engine is still called get-by-id + only, for approval ids those memos already carry. It never polls for work. +- **Not owned approval state.** Engine status is read live on each render and + never stored. Local state is limited to our own evidence: the disposition and + the submission correlation (`submissions.state`, `approved_at`). +- **Not a decision point.** The overview decides nothing. Every row goes + through the same fresh PDP `read` check that `ReviewController.open` uses + before any memo content is rendered. If that check is denied or unavailable, + the row shows the memo id and "not available" and nothing else (fail closed). +- **Not a binding view.** The overview creates no presentation record. + Binding still happens only on the existing review page. + +## Row classification (latest memo version, addressed to me) + +| Group | Condition | +|---|---| +| **Needs attention** | submission `in_flight` / `unresolved`: outcome unconfirmed, operator recovery | +| **Open for you** | no accept/decline by me on the latest version, no submission, engine status `requested`/`approved` | +| **Decided — accepted** | submission `confirmed` (show `approved_at`) plus live engine status (`approved`, `consumed`, …) | +| **Decided — declined** | decline disposition on the latest version | +| **Returned / discussion** | return/discuss on the latest version with no newer version yet (awaiting revision) | +| **Closed without you** | engine status `expired`/`revoked`/`superseded` and no disposition by me | + +Each row links to the existing review (`/review?memo_id=…`), which already +recovers the original presentation when an intent exists. The earlier +dispositions on older memo versions appear as a short history under the row. + +## Store listing for the signed-in subject + +```task +id: INFD-WP-0003-T01 +status: todo +priority: high +``` + +Add `Store.memos_for(subject)`, which returns the latest version of every memo +whose binding principal is `subject` (a `person`). Add +`Store.dispositions_by(subject, memo_id)`, which returns that subject's +dispositions across all versions, together with any submission row. This is +read-only. Tests go in `tests/test_durable_store.py`. + +## Controller overview with per-row PDP read and live engine status + +```task +id: INFD-WP-0003-T02 +status: todo +priority: high +``` + +Add `ReviewController.overview(session)`, which returns classified rows. Each +row gets its own `_authorize(session, memo, "read")` and `get_approval`. A row +whose PDP check fails is redacted to its id only. A failure of the approval +engine on one row only degrades that row's status to "status unavailable". It +never degrades the whole page. The overview never creates a presentation. Tests +go in `tests/test_review_controller.py`, using the existing fakes: allow, deny, +policy unavailable, engine down, and each classification. + +## Home page rendering + +```task +id: INFD-WP-0003-T03 +status: todo +priority: high +``` + +When a review is configured and the person is signed in, `GET /` renders the +overview grouped as above, with open items first. The manual memo-id form stays +below the overview as a fallback. This is server-rendered only: no scripts, the +same CSP, all text escaped. Tests: a WSGI test in `tests/test_browser_auth.py` +or `test_review_component.py`, plus one Chromium check in +`tests/browser_review.mjs` if that harness is cheap to extend. + +## Docs and scope + +```task +id: INFD-WP-0003-T04 +status: todo +priority: medium +``` + +Update `docs/protected-browser-review.md` and `SCOPE.md` with the overview and +the boundary argument above.