{ "cwd": "/tmp/gstack-paid-shard-s9u5nX/tmp/gstack-autoplan-chain-2fm0gB", "ownedStateRoot": "/tmp/gstack-paid-shard-s9u5nX/tmp/gstack-hermetic-1053224-0wnKD0/skill-home-Apv3Kh/.gstack", "viewportCapturedAt": 1789019855180, "pending": { "source": "pre_tool_use", "sessionId": "a6866038-b8f3-4d4b-96ef-2b7bedb87909", "toolUseId": "toolu_01MPtrmcKUsSV5jQTxotGRH4", "tool": "Edit", "file": "/tmp/gstack-paid-shard-s9u5nX/tmp/gstack-hermetic-1053224-0wnKD0/skill-home-Apv3Kh/.gstack/projects/gstack-autoplan-chain-2fm0gB/ceo-plans/2026-09-10-user-dashboard.md", "timestamp": "2026-09-10T05:55:22.812Z" }, "events": [ { "sessionId": "a6866038-b8f3-4d4b-96ef-2b7bedb87909", "timestamp": "2026-09-10T05:53:57.405Z", "toolUseId": "toolu_016zuUSxgruR3zxXqQCK18wG", "kind": "use", "name": "Write", "input": { "file_path": "/tmp/gstack-paid-shard-s9u5nX/tmp/gstack-hermetic-1053224-0wnKD0/skill-home-Apv3Kh/.gstack/projects/gstack-autoplan-chain-2fm0gB/ceo-plans/2026-09-10-user-dashboard.md" } }, { "sessionId": "a6866038-b8f3-4d4b-96ef-2b7bedb87909", "timestamp": "2026-09-10T05:54:02.558Z", "toolUseId": "toolu_016zuUSxgruR3zxXqQCK18wG", "kind": "result", "isError": false } ], "before": "---\nstatus: ACTIVE\n---\n# CEO Plan: User Dashboard Page\nGenerated by /plan-ceo-review (via /autoplan) on 2026-09-10\nBranch: main | Mode: SELECTIVE EXPANSION\nRepo: gstack-autoplan-chain-2fm0gB (no remote configured)\nBase plan: `.claude/plans/ui-heavy-feature.md` (its \"Existing product and application\ncontracts\" section defines the terms used below; this document adds decisions, not contracts).\n\n## Glossary (from the base plan)\n- **Action registry**: existing server-side list of three actions (create an item, resume assigned work, invite a member), each with a stable ID, label, route target, and an **eligibility predicate** evaluated on the server against the request context. The registry supplies labels and routes only; it does not supply item titles.\n- **Hero**: the first quick action in this fixed priority order among the eligible ones: resume assigned work, create an item, invite a member. It is rendered larger than the other quick actions and uses the registry label (so \"Resume assigned work\", never an item title). It is not a new slot and adds no new data.\n- **Bulk-read API**: existing member-scoped mutation that marks notifications at or before a supplied snapshot time as read. It already accepts the snapshot; no new mutation is added.\n- **fetchedAt**: ISO-8601 UTC timestamp set by the server when it composes the dashboard response.\n- **Exposure events**: `dashboard_viewed{panelStates}` once per page mount after first render; `dashboard_panel_rendered{panel,state}` on each transition to a terminal state.\n- **Interaction events**: `quick_action_clicked{actionId}`, `dashboard_view_all_clicked{panel}`, `dashboard_panel_retry_clicked{panel}`, `mark_all_read_confirmed{count}`, `mark_all_read_cancelled`.\n- **Current landing page**: the page members reach today after login (flag off). Its p95 is measured server-side from the existing request metrics over the same window as the dashboard measurement.\n\n## Vision\n\n### 10x Check\nThe 10x version is not a prettier dashboard. It is a landing page that already\nknows the member's next task. The moment the page paints, the hero quick action\nsits at the top: \"Resume assigned work\" when that action is eligible, otherwise\n\"Create an item\". Notifications sit second with an unread count and one-click\n\"Mark all as read\". Activity is third, a calm answer to \"what changed while I was\naway\". The page is measured, not assumed: exposure and interaction events are\nemitted as defined above, so login-to-first-completed-task is read from production\nanalytics for the flag-on cohort against a concurrent flag-off control. Effort for\nthe hero-first hierarchy over a flat three-card grid: human ~0.5 day / CC ~10 min.\nThe redirect-when-resumable variant is a separate experiment (deferred, see below)\nbecause it changes the stated direction that the dashboard is the landing page.\n\n## Layout\nTwelve-column grid from the existing page shell.\n- **sm**: single column, order QuickActions, Notifications, Activity.\n- **md**: QuickActions spans 12; beneath it Notifications 6 and Activity 6.\n- **lg**: three columns: QuickActions 5, Notifications 4, Activity 3.\n\n## Scope Decisions\n\n| # | Proposal | Effort | Decision | Reasoning |\n|---|----------|--------|----------|-----------|\n| 1 | Per-panel result envelope in `GET /api/dashboard`: each key is `{status:'ok',data}` or `{status:'error',error:{code}}`, inside an HTTP 200; HTTP 503 only when all three fail. Repository calls run in parallel with a 2 s per-panel deadline passed to each call as an abort signal; on deadline the panel yields `{status:'error',error:{code:'timeout'}}` and the call is aborted. If a repository method cannot take a signal, the leaked work is bounded by the existing database statement timeout and that bound is noted in the handler | S | ACCEPTED | In blast radius; makes partial failure explicit (P5); the deadline is what lets the latency gate hold without caching |\n| 2 | Server `fetchedAt` used as the snapshot passed to the existing bulk-read API | S | ACCEPTED | The API already takes a snapshot; using the server time preserves \"later arrivals remain unread\" against client clock skew. No new mutation |\n| 3 | `quickActions` filtered server-side by the registry eligibility predicates | S | ACCEPTED | Without it the permission-error guardrail trips by construction |\n| 4 | p95 latency of `GET /api/dashboard` at or below the current landing page's server-side p95, both measured over 1000 requests in staging; rollout gate | S | ACCEPTED | Plan already says perf checks must be specified; this specifies them |\n| 5 | Production baseline (p50 and p90 login-to-first-completion from existing login and action-completion events) pulled before rollout; required members per arm computed from the baseline variance for detecting a 20% p50 change at 80% power before the cohort starts; flag `dashboard_landing` assigned randomly per member; success = p50 improves by at least 20% vs the concurrent flag-off cohort over 14 days (or until the required N is reached, whichever is later), p90 reported as a secondary metric, completed-task rate and permission-error rate within 2 points of control | S | ACCEPTED | Instrumentation is already in scope; the walkthrough number is not a control; a threshold and a sample size are needed to call the result |\n| 6 | Toast built as a shared primitive beside the dialog primitive: `aria-live` region (polite for success, assertive for failure), at most three visible, no transform animation under `prefers-reduced-motion` | S | ACCEPTED | DRY (P4); a dashboard-owned toast becomes the app toast and gets rewritten |\n| 7 | Shared `usePanelQuery` hook. Error handling in three tiers: (a) a per-panel `{status:'error'}` envelope puts only that panel in error; (b) a non-auth transport failure (network, 5xx, malformed body, validation) puts every panel in error with Retry; (c) an auth transport failure is page-level: 401 redirects to login via the existing unauthenticated handling, 403 renders the full-page access message and no panels. Aborts on unmount; discards responses whose sequence number is lower than the last completed mutation; refetches on `visibilitychange` | S | ACCEPTED | Three panels with identical state mapping; one hook, three consumers |\n| 8 | Hero-first hierarchy with the layout above | S | ACCEPTED | Hierarchy as service: the metric is first completed task, so the action is first |\n| 9 | Mark-all-read behaviour: no optimistic update. On success, increment the sequence, refetch Notifications, close the dialog, polite toast \"Marked N as read\" with the server-reported count. On failure, assertive toast \"Couldn't mark as read. Try again.\", dialog stays open, no local change | S | ACCEPTED | Explicit over clever; the refetch is what the sequence counter protects |\n| 10 | Delight touches (five) plus the all-empty case: relative timestamps with absolute `title`; skeletons sized to final row heights (no layout shift); \"Mark all as read\" disabled at zero unread; empty states cross-link to the relevant quick action; \"View all\" links to the full pages. When all three panels are empty (new member) the hero copy becomes \"Create your first item\" and the other panels show their empty copy; no separate welcome screen | S | ACCEPTED | Each under 30 min CC, all inside the panel files already being written |\n| 11 | Panel-registry response shape (array of typed panels) for future personalization | M | DEFERRED | Personalization is a separate plan; building the abstraction now is speculative (P5). Mitigation: panels are independent components so reordering later is trivial |\n| 12 | Post-login redirect straight to the resume route when that action is eligible; dashboard only as fallback | M | DEFERRED | Changes the stated direction (dashboard is the landing page). Single-voice finding. Surfaced as a taste decision at the gate; queued as an experiment |\n| 13 | Unread count in document title / nav badge | S | DEFERRED | Touches the app shell outside the blast radius of this plan |\n| 14 | Replace the confirm modal with undo | M | SKIPPED | Requires an un-read mutation that does not exist; base plan says no new mutation API |\n\n## Edge cases resolved\n- Refetch racing a pending mark-all-read: the hook's sequence counter discards the older response; the unread count cannot flash back.\n- 401 and 403 during dashboard load: page-level per decision 7(c); no panels render.\n- All three panels empty: see decision 10.\n- Multiple assigned items: the registry's resume action is a single route into the existing workflow; the hero shows the registry label and the route decides where the member lands. No item selection happens on the dashboard.\n- Flag and analytics infrastructure: both exist per the base plan (staging feature flags, request/error metrics, login and action events); nothing new is built for them.\n\n## Accepted Scope (added to this plan)\n- Per-panel result envelopes with per-panel deadlines and explicit partial-failure semantics; 503 only on total failure\n- `fetchedAt` server timestamp as the bulk-read snapshot\n- Server-side eligibility filtering of quick actions; hero priority resume, create, invite\n- p95 latency budget as a rollout gate\n- Production baseline, required sample size, random per-member flag assignment, concurrent control cohort, and a stated success threshold with p90 secondary\n- Shared toast primitive with live region, cap, and reduced-motion support\n- Shared `usePanelQuery` hook with three-tier error handling and an explicit panel-state union\n- Mark-all-read refetch-on-success behaviour\n- Hero-first hierarchy and the sm/md/lg twelve-column layout\n- Five delight touches plus the all-empty new-member case\n- Exposure and interaction events as named in the glossary\n\n## Deferred to TODOS.md\n- Panel-registry response shape (revisit when the personalization plan starts)\n- Resume-redirect experiment (flagged, compared against the dashboard cohort)\n- Unread count in document title / nav badge (app shell change)\n", "viewport": "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n Edit file\n \u20261053224-0wnKD0/skill-home-Apv3Kh/.gstack/projects/gstack-autoplan-chain-2fm0gB/ceo-plans/2026-09-10-user-dashboard.md\n\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\n 10 \n 11 ## Glossary (from the base plan)\n 12 - **Action registry**: existing server-side list of three actions (create an item, resume assigned work, invite a m\n ember), each with a stable ID, label, route target, and an **eligibility predicate** evaluated on the server agains\n t the request context. The registry supplies labels and routes only; it does not supply item titles.\n 13 -- **Hero**: the first quick action in this fixed priority order among the eligible ones: resume assigned work, crea\n -te an item, invite a member. It is rendered larger than the other quick actions and uses the registry label (so \"Re\n -sume assigned work\", never an item title). It is not a new slot and adds no new data. \n 14 -- **Bulk-read API**: existing member-scoped mutation that marks notifications at or before a supplied snapshot time\n - as read. It already accepts the snapshot; no new mutation is added. \n 13 +- **Hero**: the first quick action in this fixed priority order among the eligible ones: resume assigned work, crea\n +te an item, invite a member. It is rendered larger than the other quick actions and uses the registry label (so \"Re\n +sume assigned work\", never an item title), with one client-side copy substitution: in the new-member case (below) t\n +he create action's label is shown as \"Create your first item\". It is not a new slot and adds no new data. \n 14 +- **New-member case**: resume is not eligible (so create is the hero) and both Notifications and Activity are empty\n +. \n 15 +- **Bulk-read API**: existing member-scoped mutation that marks notifications at or before a supplied snapshot time\n + as read. It already accepts the snapshot; no new mutation is added. The base plan does not state a return value, s\n +o the \"Marked N as read\" count is the client's unread count from the last dashboard response, not a server-reported\n + number. \n 16 +- **Refetch**: a refetch (after mark-all-read or on `visibilitychange`) keeps the current data on screen, does not \n +re-enter the loading state, and re-emits `dashboard_panel_rendered` only if the panel's state value changes (for ex\n +ample success to empty). \n 17 - **fetchedAt**: ISO-8601 UTC timestamp set by the server when it composes the dashboard response.\n 18 - **Exposure events**: `dashboard_viewed{panelStates}` once per page mount after first render; `dashboard_panel_ren\n dered{panel,state}` on each transition to a terminal state.\n 19 - **Interaction events**: `quick_action_clicked{actionId}`, `dashboard_view_all_clicked{panel}`, `dashboard_panel_r\n etry_clicked{panel}`, `mark_all_read_confirmed{count}`, `mark_all_read_cancelled`.\n\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\u254c\n Do you want to make this edit to 2026-09-10-user-dashboard.md?\n \u276f 1. Yes\n 2. Yes, and switch to accept edits (auto-approve file edits and common file commands) for this session (shift+tab)\n 3. No\n\n Esc to cancel \u00b7 Tab to amend\n" }