|
| 1 | +# Accepted native Android Question–Chat workspace |
| 2 | + |
| 3 | +**Planning ticket:** [Prototype the native Question–Chat workspace](https://github.com/dasomji/pi-postbox/issues/61) |
| 4 | + |
| 5 | +**Human decision:** **A — Workspace tabs**, accepted through Pi Postbox on 2026-07-29. |
| 6 | + |
| 7 | +## Prototype question |
| 8 | + |
| 9 | +Which native Compose information architecture keeps a pending Postbox Question and its temporary Question Chat clear through activation, fallback, streaming, recovery, and return-to-answer on a narrow Android screen? |
| 10 | + |
| 11 | +A throwaway interactive browser prototype compared three structurally different mobile models: |
| 12 | + |
| 13 | +1. **Workspace tabs:** the Postbox Question remains primary before activation; afterward, Question and Question Chat become peer tabs. |
| 14 | +2. **Question Chat sheet:** Question Chat lives in a near-full-height modal sheet over the Postbox Question. |
| 15 | +3. **Decision companion:** Question Chat becomes primary beneath a persistent compact decision rail, with the Postbox Question reopened in a review sheet. |
| 16 | + |
| 17 | +The human selected **Workspace tabs**. It has the clearest native navigation and accessibility model, keeps the authoritative answer surface distinct from guidance, avoids modal-sheet/IME/nested-scroll complexity, and makes the return from a Chat-suggested option explicit. |
| 18 | + |
| 19 | +## Accepted interaction model |
| 20 | + |
| 21 | +### Before activation |
| 22 | + |
| 23 | +- Show no workspace tabs. The existing Postbox Question remains the full primary surface. |
| 24 | +- Place a secondary **Start Question Chat** action after the question context and before answer options. Its supporting copy says Question Chat is temporary and first attempts an exact fork. |
| 25 | +- Starting Question Chat never changes or submits a draft answer. |
| 26 | + |
| 27 | + |
| 28 | + |
| 29 | +### Exact-fork failure and context-only fallback |
| 30 | + |
| 31 | +- Keep the user on the Postbox Question. |
| 32 | +- Show the server-provided exact-fork failure as an inline warning with one **Consider context-only interviewer** action. |
| 33 | +- That action opens a native confirmation surface with the full distinction: a context-only interviewer starts fresh from persisted handoff context and is not an exact fork of the originating Pi Session. |
| 34 | +- Confirmation is explicit and cancellable. Cancel leaves the Question unchanged. |
| 35 | +- After confirmation succeeds, reveal the tabs, select **Question Chat**, and keep a persistent degraded/context-only disclosure in that tab. |
| 36 | +- If the server says context fallback is unavailable, show the reason and Retry/return path without offering confirmation. |
| 37 | + |
| 38 | + |
| 39 | + |
| 40 | +### Activated workspace |
| 41 | + |
| 42 | +- Add a two-item native tab row directly below the existing app bar: **Question** and **Question Chat**. |
| 43 | +- Each tab owns one full-width, independently scrollable panel. Do not use a narrow split pane in this implementation slice. |
| 44 | +- Preserve the Question draft selection and note while moving between tabs. |
| 45 | +- Preserve Question Chat scroll/state through ordinary in-memory owner state; do not persist its transcript or draft. |
| 46 | +- When Question Chat is selected, Android Back first returns to **Question**. Normal app navigation/back behavior resumes from the Question tab. |
| 47 | +- Tab state is keyed with the current server and request ID and resets when either changes. |
| 48 | + |
| 49 | +### Empty Question Chat |
| 50 | + |
| 51 | +- Show a concise explanation that Question Chat can help understanding but cannot answer the Postbox Question. |
| 52 | +- Present the three deterministic starters: **Elaborate**, **Pro–Cons**, and **Teach me**. |
| 53 | +- Keep the freeform composer visible at the bottom. |
| 54 | +- Label the exact-fork/context-only mode and model without making model metadata the primary hierarchy. |
| 55 | + |
| 56 | +### Streaming, steer, tools, and Stop |
| 57 | + |
| 58 | +- Stream assistant content into a message list above an anchored composer. |
| 59 | +- During generation, keep the composer enabled and relabel its action **Steer**. Supporting text explains that a new message steers the active Question Chat turn. |
| 60 | +- Put **Stop** beside the coarse active-turn status, not inside assistant prose. |
| 61 | +- Show bounded tool activity as a separate collapsed row/card. It is not assistant Markdown and must not dominate the transcript. |
| 62 | +- Announce only coarse state transitions such as Answering, Stopped, Interrupted, Offline, and Ready; the changing assistant body is not a live region. |
| 63 | + |
| 64 | + |
| 65 | + |
| 66 | +### Stopped or interrupted output |
| 67 | + |
| 68 | +- Retain the bounded readable assistant prefix. |
| 69 | +- Mark the message itself **Stopped** or **Interrupted** and also expose the coarse workspace state. |
| 70 | +- Keep the composer available for a later Question Chat turn when the authoritative state permits it. |
| 71 | + |
| 72 | + |
| 73 | + |
| 74 | +### Offline and recovery |
| 75 | + |
| 76 | +- Keep the last authoritative synchronized transcript visible. |
| 77 | +- Show one inline **Question Chat offline** or resynchronizing banner with **Retry**. |
| 78 | +- Disable composer, Steer, and Stop until the owner is synchronized and online. |
| 79 | +- Do not describe in-memory content as “saved”; use “last synchronized messages.” |
| 80 | + |
| 81 | + |
| 82 | + |
| 83 | +### Chat-suggested option return |
| 84 | + |
| 85 | +- Render a server-authoritative proposal as a separate **Suggested in Chat** card with **Review in Question**. |
| 86 | +- The action selects the **Question** tab and exposes/highlights the authoritative option there. |
| 87 | +- It never submits automatically. Existing single/multi validation and the normal answer action remain authoritative. |
| 88 | +- Announce that the suggestion is ready for review and not submitted. |
| 89 | + |
| 90 | +| Suggested in Question Chat | Returned to authoritative Question | |
| 91 | +|---|---| |
| 92 | +|  |  | |
| 93 | + |
| 94 | +## Narrow screens and IME |
| 95 | + |
| 96 | +- Use the same tab information architecture at 360 dp and larger; do not introduce a second navigation model by width. |
| 97 | +- Give the message list the remaining bounded height and keep the composer above system bars/IME using Compose insets. |
| 98 | +- Focusing the composer must bring it into view without replacing tab selection or losing message scroll identity. |
| 99 | +- Starters wrap rather than clip. Status/action rows may wrap at narrow widths. |
| 100 | +- A visible IME must not cover Send/Steer/Stop or create two competing vertical scroll containers. |
| 101 | + |
| 102 | + |
| 103 | + |
| 104 | +## Compose contract for later planning |
| 105 | + |
| 106 | +The prototype settles information architecture, not production component code. The rendering/state tickets should preserve these seams: |
| 107 | + |
| 108 | +```text |
| 109 | +Question detail (pre-activation) |
| 110 | + -> exact activation attempt |
| 111 | + -> inline availability failure + explicit fallback confirmation |
| 112 | + -> activated Question | Question Chat tab workspace |
| 113 | +
|
| 114 | +Question Chat suggestion |
| 115 | + -> authoritative question/proposal update |
| 116 | + -> select Question tab |
| 117 | + -> highlight valid option |
| 118 | + -> ordinary answer submission only |
| 119 | +``` |
| 120 | + |
| 121 | +Recommended native component shape: |
| 122 | + |
| 123 | +- existing app bar and postal theme; |
| 124 | +- Material 3 primary tab row with selected semantics; |
| 125 | +- independently keyed `LazyColumn`/Question panel beneath it; |
| 126 | +- anchored multiline composer with IME/navigation-bar insets; |
| 127 | +- native confirmation dialog or modal bottom confirmation surface for context-only consent; |
| 128 | +- Snackbar/status semantics for send-versus-steer acknowledgement and proposal return; |
| 129 | +- minimum 48 dp actions and meaningful tab, state, Stop, Retry, and proposal labels. |
| 130 | + |
| 131 | +The dedicated Question Chat owner selected by issues 59–60 remains responsible for server/request identity, lifecycle, bounded snapshots/events, safe Markdown documents, and strict in-memory privacy. Compose receives finite presentation state and emits user intents; it does not own transport or transcript persistence. |
| 132 | + |
| 133 | +## Accessibility acceptance points |
| 134 | + |
| 135 | +- Tabs expose selected state and distinct Question/Question Chat labels. |
| 136 | +- Focus enters the confirmation surface and returns to its invoking action when cancelled. |
| 137 | +- Switching tabs does not reset the Question draft or steal focus into progressively changing assistant text. |
| 138 | +- Coarse status changes are announced once; streamed body replacements are not live-region announcements. |
| 139 | +- Tool rows, safe links, Stop, Retry, starters, composer, and proposed-option return have explicit labels and native touch targets. |
| 140 | +- The selected Chat-suggested option exposes both selected state and provenance without relying on color. |
| 141 | + |
| 142 | +## Prototype disposition |
| 143 | + |
| 144 | +The interactive three-variant implementation was intentionally throwaway and made no network requests or persistent writes. The selected behavior, rationale, and visual evidence are retained here; losing variant code should be removed rather than promoted to production. |
0 commit comments