mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-16 09:55:29 +02:00
* feat: add a restricted and supervised Claude Code runner Preserve configured authentication and models while enforcing tool access, strict completion JSON, bounded output and process cleanup. Cover argv, failure handling, session metadata and Windows process containment. * feat: route outside reviews by harness and migrate wrapper installs Use Claude Code from Codex and Codex from other supported hosts, with shared invocation rendering, positive gate validation and per-phase provenance. Rename /claude to /claude-code, repair managed shared and copied installations safely, and generate native Kiro skills. Add installed-workflow, failure-injection and live cross-harness regression coverage. * test: recognize CEO mode labels without terminal spacing The paid workflow rendered SCOPEEXPANSION at option 4, but its driver required a literal space. Match the leading mode title without cursor-spacing artifacts and ignore adjacent preview text. Preserve missing-target failures and downstream posture assertions. * test: isolate plan-count fixtures before starting review workflows Seed the complete test plan in a private git repository before launching Claude, so a bare slash command cannot review the live workspace while a delayed fixture message remains queued. Preserve count thresholds, parsers and budgets. Add initial-context and installed-discovery tests, and retain startup/terminal diagnostics on failed evaluations. * test: stabilize review fixtures and Claude eval startup Preserve source boundaries in workflow judge inputs, isolate CEO mode plans, and wait for interactive trust input readiness. Keep startup failure evidence and retain existing models, budgets, and assertions. Co-Authored-By: OpenAI Codex <noreply@openai.com> * test: classify collapsed review modes and isolate seeded findings Keep review questions out of the setup count when terminal cursor positioning removes spaces. State existing webhook safeguards so the five-finding control measures its seeded defects without accidental extra security and concurrency gaps. Preserve question bands and the paired control. Co-Authored-By: OpenAI Codex <noreply@openai.com> * test: isolate browser daemon state across free shards Co-Authored-By: OpenAI Codex <noreply@openai.com> * test: stabilize native review counting and interactive navigation Co-Authored-By: OpenAI Codex <noreply@openai.com> * chore: prepare v1.82.0.0 release Co-Authored-By: OpenAI Codex <noreply@openai.com> * fix: eliminate browser and process-cleanup test flakes Pin every CI surface to Bun 1.4.0 to avoid extra-stdio finalizers closing reused live sockets. Add an isolated GC/listener regression that fails on Bun 1.3.13, and prevent coordinated rollback to an affected CI runtime. Check renderer cleanup against the render's own staging directory so concurrent renders cannot invalidate the assertion. Make the no-pgrep process-tree walk tolerate disappearing /proc entries, and synchronize its test fixture through child readiness and pipe EOF instead of sleeps. Validation: 9,157 passed, 31 skipped, zero failures across 556 files with retries disabled. Build, all-host generation freshness, and skill checks passed. All three races have failing-before/passing-after regressions. * fix: count completed native review questions in evals * fix: drive review navigation from confirmed native choices * fix: require complete section-loading eval reports * test: isolate telemetry HTTP transport from local assertions * fix: keep review input on the active native question * test: let tunnel revocation daemon choose an available port * test: allocate available ports for pairing and watchdog fixtures * fix: stabilize planning eval navigation and phase reporting * test: isolate installed runtime paths in planning evals * test: stabilize review evidence and concurrent refresh fixtures * fix: resolve design findings before editing the plan * fix: honor and persist disabled outside plan reviews * fix: preserve planning decisions and terminal evidence Load installed host reviews at autoplan phase entry and wait for completed reviewers and saved artifacts. Reuse approved remedies while preserving individual finding decisions. Drive interactive evals from the current terminal viewport, bind native questions across scrolling, and require complete native report evidence. Cover captured stale menus, permission lifecycles, setup classification, and disabled-review tool availability with deterministic regressions. Advance release metadata and the upgrade migration to the unclaimed 1.83.0.0 slot. * fix: drive native review questions and preserve current plans Use the native single-choice keyboard protocol and current terminal viewport, with per-question navigation inside packets and completed-call coverage. Keep permissions, multi-select menus, and Submit controls distinct. Send Autoplan reviewers the amended implementation plan, keep its review record separate, and supply retained application contracts in the chain fixture. Clarify individual DevEx decisions and complete CEO fix options; use one active plan destination for the section-loading report. * fix: preserve complete plan-review decisions * fix: recognize native plan dialogs and reviewer controls * fix: preserve review decisions and phase completion * fix: recognize completed reviews without losing findings * fix: preserve review continuity and native eval completion * test: fix native review completion and eval retry isolation * test: handle native review menus and complete eval fixtures * test: fix native review setup, completion, and isolation failures * test: limit native skill discovery to runtime assets * fix: bind Autoplan reviews to full ordered phase inputs * test: fix planning eval routing, counting, and timeout handling * chore: advance queued release to v1.84.0.0 * fix: preserve complete review inputs and planning decisions * fix: reconcile review approvals and preserve phase obligations * fix: preserve review obligations and unblock eval permissions Carry recorded Autoplan requirements into blind phase inputs, require Eng review approvals before exit, and exercise combined asynchronous flows in CEO reviews. Correct native finding and handoff classification and unblock repeated report edits using scoped request identities. * fix: retain plan requirements and complete native review dialogs * fix: complete native review prompts and retain plan references * fix: preserve review inputs and classify native eval evidence * fix: check competing completion orders in CEO reviews * fix: recognize review decisions and require phase methodology Require the current phase methodology before Autoplan snapshots. Correct substantive decision, closed handoff, and cache-finding classification, and honor the recommended implementation approach in native review dialogs. Add captured-transcript regressions without changing review thresholds, provider models, retries, or deadlines. * test: bind native review decisions and close completed handoffs * fix: complete review dialogs and verify methodology delivery * fix: preserve review evidence and unblock native eval prompts * fix: handle native review question completions * fix: recognize native review narration and controls * fix: count native review decisions and isolate eval fixtures * test: verify seeded review coverage and current artifact permissions * test: isolate model and brain-aware skill renders * fix: repair native workflow evaluation and clarify review steps * fix: stabilize workflow eval evidence and review guidance * test: repair native workflow observation and fixture isolation * fix: recognize completed workflow evidence and owned skill reads * test: repair seeded workflow delivery and completion evidence * test: recognize current review evidence across native forms * test: handle native review variants and permission redraws * fix: honor review preferences and recognize native eval evidence * test: recognize completed review decisions and queued permissions * test: match current review contracts and partial-line edits * test: recognize completed workflow evidence and bounded human waits * fix: preserve review entry gates and native eval interactions * fix: recognize native workflow evidence and preserve review gates * test: recognize current review evidence and preconfigure workflow fixtures * test: recognize completed review findings and scoped artifact permissions * fix: stabilize native workflow review and permission evidence * fix: recognize current review evidence and scoped edit confirmations Clarify Design and engineering review entry instructions and Design scoring. Recognize required legacy coverage and public Autoplan completion recaps. Bind the pending Edit confirmation to its exact file, ordered digest, and one-request approval when a preceding command display remains visible. Keep reviews within their existing size limits and preserve scope gates when extracting workflow fixtures from either supported preamble header. Keep failure outcomes, review thresholds, provider choices, and eval budgets. * fix: recover review workflow progress and eval evidence * fix: recognize valid review evidence and scope selection * test: fix review evidence parsing and repeated artifact prompts * test: recognize valid review decisions and pending native cards * fix(plan-eng-review): keep final navigation consistent with approved tasks * test: recognize valid review evidence and bind legacy diff requests * fix: stabilize review eval evidence and harness repair guidance * docs: update project documentation for v1.85.0.0 Co-Authored-By: OpenAI Codex <noreply@openai.com> * test: fix Windows CI fixtures and credential scan Rebase captured JSON values and filesystem evidence using the appropriate path convention. Compile native fake CLIs on Windows and synchronize pipe holder readiness, with cleanup retained when assertions fail. Assemble synthetic credential fixtures at runtime so the added-line scan keeps enforcing the same gate without flagging its own rejection controls. Discover generated skills directly for the empty-find regression check, avoiding a recursive scan through saved evaluation artifacts and dependencies. * fix: preserve source renders on Windows Compare canonical generator paths using native separators so an output sidecar pointing at the source cannot overwrite its skill or metadata. Keep the regression fixture isolated from the real checkout and expose freshness diagnostics before asserting subprocess status. Detach Windows drain-test pipe holders from the fake provider's automatic child cleanup while preserving the enclosing runner job and its assertions. * fix: clarify outside review fallback and CEO decisions Render one applicable own-harness fallback path and retain native review, disabled policy, and missing-coverage semantics. Align report field names and mode labels, and make the existing per-cut scope approval explicit. Regenerate skill outputs and keep the workflow judge's model, thresholds, and retry policy unchanged. * chore: move release to free version slot (v1.86.0.0) PR #2852 now claims v1.85.0.0. Align the release metadata and rename migration so upgrades from that version still receive it. Co-Authored-By: OpenAI Codex <noreply@openai.com> * fix: include engineering review prerequisites and restore branch context * fix: recognize coverage diagrams and clarify design review instructions * fix: preserve file identities and join Windows test processes --------- Co-authored-by: OpenAI Codex <noreply@openai.com>
47 lines
42 KiB
JSON
47 lines
42 KiB
JSON
{
|
|
"provenance": {
|
|
"sourceHead": "c73102357cbc3466d6a3c8d3ad0ac7e3177ce62c",
|
|
"proofSHA256": "fdb3a655c14b5754dae440ba0255f4da9e1247a3557b68f85d5f7afe43bc750c",
|
|
"nativeSHA256": "ecca735afd983490f0ea44e93541b1a5694479205522910d5ebb4a2cea2b16f9",
|
|
"scope": "Exact current pane, before file, pending identity, and latest successful same-file public Write+ack. Original pending Edit input is unpublished and unavailable.",
|
|
"reconstruction": "Synthetic insertion request uses the exact current visible ten-line tail only; it is not the original unretained Edit body or paid approval evidence."
|
|
},
|
|
"cwd": "/tmp/gstack-paid-shard-tVuvKy/tmp/gstack-autoplan-chain-tcPo1b",
|
|
"ownedStateRoot": "/tmp/gstack-paid-shard-tVuvKy/tmp/gstack-hermetic-1626312-olYxKb/skill-home-deJ6wd/.gstack",
|
|
"pending": {
|
|
"source": "pre_tool_use",
|
|
"sessionId": "a828fa7a-3d8e-48cd-ae1f-e3a9cf0611b2",
|
|
"toolUseId": "toolu_01NUtCP9y2fRrv6wtKyje5EY",
|
|
"tool": "Edit",
|
|
"file": "/tmp/gstack-paid-shard-tVuvKy/tmp/gstack-hermetic-1626312-olYxKb/skill-home-deJ6wd/.gstack/projects/gstack-autoplan-chain-tcPo1b/ceo-plans/2026-09-10-user-dashboard.md",
|
|
"timestamp": "2026-09-10T08:15:50.752Z"
|
|
},
|
|
"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-tcPo1b (local, no remote)\n\n## Step 0 \u2014 Premise P0: verify the \"existing\" contracts before building\n\nThe repository reviewed contains no application code (only README.md and the plan). Every \"existing\" item below is taken from the plan author's contract section and is unverified. Before any implementation task starts, confirm each exists and behaves as stated:\n\n- [ ] Cookie session + workspace membership middleware; request context exposes `memberId` and `workspaceId`\n- [ ] Activity and notification list repository methods: latest 20 + cursor, indexed on workspace/member + created_at; note whether they accept a `limit` argument and an `AbortSignal`\n- [ ] Existing item types `ActivityItem`, `NotificationItem` (must carry a read flag such as `readAt`), `QuickAction` (id, label, routeTarget) as used by the current activity/notification pages and the action registry\n- [ ] A notifications unread-count read method, or an indexed `(member_id) WHERE read_at IS NULL` path that a one-line COUNT can use\n- [ ] Action registry: 3 actions with stable IDs, labels, route targets, server-side eligibility predicates\n- [ ] **Existing** member-scoped bulk-read mutation (working name `POST /api/notifications/read-all`): idempotent, marks notifications with `created_at \u2264 snapshotAt`, requires CSRF token\n- [ ] Typed HTTP client errors: unauthenticated, forbidden, validation, retryable-service, network\n- [ ] Dialog primitive with focus trap, Escape dismissal, focus return; Tailwind tokens; responsive page shell; motion tokens or a `prefers-reduced-motion` utility\n- [ ] Vitest + React Testing Library + Playwright in CI; fixtures for member, other-workspace, empty lists, service failures\n- [ ] Staging feature flags with member-cohort targeting (two independent boolean flags can be created); request/error metrics\n- [ ] Existing analytics events: login, action start, action completion, permission error; confirm they accept a context/source attribute (needed for `source: 'dashboard'`)\n- [ ] Existing full pages for activity and notifications (targets of the \"View all\" links); existing post-login redirect code and its `returnTo` handling\n\nIf any box fails, the Eng phase reopens the affected decision before implementation. Known fallbacks: if repos do not accept an `AbortSignal`, a thin signal-aware wrapper (`runSubQuery`) enters blast radius and the timeout is a `Promise.race` that abandons the still-running query (accepted: bounded user wait, not bounded DB work). If no unread-count method exists, a single indexed COUNT query is added inside `DashboardService` (a read, no schema change).\n\n## Success criteria\n\nMetric: median time from login to first completed task. Baseline: 75s (team walkthrough, small sample). Target: 45s. Guardrails: completed-task rate must not regress overall or for the high-frequency member segment; permission-error rate for actions started from the dashboard must not exceed the permission-error rate for the same actions started from the current three-page flow (control cohort). Existing action start / completion / permission-error events gain a `source: 'dashboard' | 'legacy'` attribute so both comparisons are possible. Baseline instrumentation of the current three-page flow runs in parallel with the build and must be live before the first redirect step.\n\n## Vision\n\n### 10x Check\nThe login surface that knows what you should do next: assigned work ranked by urgency, alerts that block that work, and a digest of what changed since your last visit, each one a single keypress away. This plan builds the home surface and the two shared primitives (toast, confirm dialog) that version needs; the ranking and digest services are explicitly deferred to the personalization plan.\n\n## Approach \u2014 PROVISIONAL pending T2 (aggregate endpoint) at the Final Gate\n\nOne aggregate `GET /api/dashboard`, no request parameters, scoped from request context only. Three sub-queries run in parallel through `runSubQuery(name, fn, signal)` and `Promise.allSettled`.\n\n```ts\ntype Envelope<T> = { ok: true; data: T } | { ok: false; error: { code: 'timeout' | 'unavailable' } };\ntype DashboardResponse = {\n activity: Envelope<ActivityItem[]>; // 5 items\n notifications: Envelope<NotificationItem[]> & { snapshotAt: string; unreadCount: number }; // 5 items\n quickActions: Envelope<QuickAction[]>; // 0-3 items\n meta: { serverTime: string; requestId: string };\n};\n```\n\nRow limits: each list panel shows 5 rows. The endpoint passes `limit: 5` if the repository method accepts it; otherwise it slices the returned 20 server-side. `unreadCount` is the member's total unread count, not the count within the 5 rows. `snapshotAt` is the database clock (`SELECT now()`) taken in the same round trip as the notifications query, so app-instance clock skew cannot mark or miss notifications incorrectly.\n\nError boundary rule: `Promise.allSettled` never throws, so the handler classifies each settled result explicitly: fulfilled \u2192 `ok:true`; rejected with `RepoTimeoutError` (from the per-sub-query `AbortSignal.timeout`, or the race fallback) \u2192 `ok:false, code:'timeout'`; rejected with `ServiceUnavailableError` \u2192 `ok:false, code:'unavailable'`; rejected with anything else \u2192 rethrown after settlement so the framework returns 500 with `requestId`. The response is HTTP 200 whenever auth and membership pass, even if all three envelopes are `ok:false`. Pre-fan-out failures return non-200: no session \u2192 401, not a member \u2192 403. A throwing eligibility predicate omits that one action, logs a warning with `actionId`, and increments `dashboard_predicate_error_total`.\n\nLatency: p95 budget for the endpoint is \u2264300ms in staging at 50 concurrent logins. The 2000ms per-sub-query cap is a hang ceiling, not a latency target; a sub-query that hits it renders a panel error and `dashboard_panel_status{status=timeout}` alerts. Eng phase may lower the cap to 1000ms on staging evidence.\n\nData fetching: one `useDashboardQuery` hook (plain `fetch` + `AbortController`, no new library) holding `{ data, error, isFetching, updatedAt }`. Retry on any panel re-invokes the hook; because the endpoint is single and parameterless, all three panels re-enter loading together. On tab focus it refetches if at least 30s have passed since `updatedAt`; there is no polling and no ETag in v1. A header line under the page title shows \"Updated just now\" / \"Updated 3m ago\" from `updatedAt`. On refetch failure the last successful data stays, and the header line becomes \"Couldn't refresh\" until the next successful fetch; this is inline text, not a toast, and not the panel error state. A 401 during a focus refetch redirects to login exactly like the initial load.\n\nPanel render states (shared `usePanelState` hook, copy in `dashboardCopy.ts`):\n\n| Panel | Loading | Empty (`ok:true, data:[]`) | Error (`ok:false` or fetch error) | Success |\n|---|---|---|---|---|\n| QuickActions | 3 fixed-height pill skeletons | \"No actions available\" | \"Couldn't load actions\" + Retry | 1-3 action buttons |\n| NotificationsPanel | 5 fixed-height row skeletons | \"You're all caught up\" | \"Couldn't load notifications\" + Retry | 5 rows + \"Mark all as read\" + \"View all\" |\n| ActivityFeed | 5 fixed-height row skeletons | \"No activity yet\" | \"Couldn't load activity\" + Retry | 5 rows + \"View all\" |\n\nRows are fixed height with a two-line clamp and full text in `title`, so skeleton geometry matches final rows. A brand-new member sees three empty states, never a blank page. Panel order is QuickActions \u2192 NotificationsPanel \u2192 ActivityFeed at every breakpoint (stacked on sm; two-column on lg with activity on the right).\n\n### Mark all as read \u2014 PROVISIONAL pending T3 (modal vs undo) at the Final Gate\n\n`ConfirmDialog` (composed on the existing dialog primitive) confirms the **existing** bulk-read mutation, posting the last response's `notifications.snapshotAt` plus the CSRF token. No new mutation API is introduced; that is why undo (which would need an inverse mutation) is deferred. The trigger button is disabled when `unreadCount === 0` and Confirm is disabled while submitting. Cancel and Escape remain available while the request is in flight; the in-flight request is aborted and its result ignored. Failures stay inline in the modal and re-enable Confirm: 401 \u2192 redirect to login; CSRF/403 \u2192 \"Session expired, reload\"; validation/400 (malformed or stale `snapshotAt`) \u2192 \"Couldn't mark as read. Reload and try again.\"; network/5xx \u2192 \"Couldn't mark as read. Try again.\" Success closes the modal, returns focus to the trigger, refetches the dashboard, and shows a success toast.\n\n### Toast (DECIDED)\n\n`ToastProvider` + `useToast` mounted at the app root (an explicit, minimal shared-code touch), `aria-live=\"polite\"`, 5s auto-dismiss paused on hover/focus, Escape and close button dismiss, max 3 visible with the oldest evicted first, reduced-motion variant using the existing motion utility. v1 emitter: mark-all-read success only. It is built as an app-root primitive on purpose: the plan names a \"toast notification system\", no toast code exists today, and mounting it at the root prevents a second ad-hoc toast implementation in the next feature.\n\n### Blast radius (definition used for cherry-pick decisions)\n\nThe new dashboard route, page, panels, hooks, `DashboardService`/handler, `runSubQuery`, the two new primitives (`ToastProvider`, `ConfirmDialog`), their tests, and two explicit minimal touches to shared code: the `ToastProvider` mount at the app root and the post-login redirect hook. Existing repos, existing APIs, the page shell's navigation, and existing pages are outside it.\n\n## Rollout (DECIDED)\n\nTwo member-cohort flags: `dashboard_route` (makes `/dashboard` reachable by URL) and `dashboard_redirect` (sends members there after login). Redirect precedence: an explicit `returnTo`/deep link always wins; the dashboard redirect applies only when there is no return URL. Flag-off behavior: a member currently on `/dashboard` when `dashboard_route` turns off is redirected to the current landing page on next navigation or fetch (the route renders a redirect, not a 404).\n\nDeploy order: (1) endpoint dark (`dashboard_route` off everywhere); (2) `dashboard_route` on for the internal cohort, no redirect; (3) `dashboard_redirect` at 5% \u2192 25% \u2192 100% of members, each step held for at least 24h and at least 500 `dashboard_view` events with guardrails flat. Kill switch and rollback: `dashboard_redirect` off returns members to the current landing page in seconds; `dashboard_route` off removes the page; the endpoint can stay deployed. Post-deploy checklist per step: non-zero `dashboard_request_total`, zero 5xx, all panel statuses ok, one manual keyboard pass; synthetic login \u2192 GET /api/dashboard smoke test.\n\n## Test acceptance (minimum)\n\nEndpoint: 200 with three ok envelopes; 200 with all three failed; unknown error \u2192 500 with requestId; 401 and 403 pre-fan-out; `?workspaceId=` ignored; `unreadCount` and `snapshotAt` present; `limit` 5 honored. Panels: loading / empty / error+Retry / success for each of the three; Retry reloads all. Modal: confirm, cancel, Escape (including in flight), double-click \u2192 one request, 401, csrf/403, validation, network; trigger disabled at `unreadCount` 0; focus returns to trigger. Ordering: notification inserted between GET and POST stays unread. Toast: cap 3 oldest evicted, hover pause, Escape, reduced motion. Hook: 30s focus interval, failure keeps data and shows \"Couldn't refresh\", 401 redirects. E2E: both flags off/on combinations, `returnTo` precedence, keyboard-only modal path, axe per panel state, prefers-reduced-motion. Staging: p95 \u2264300ms at 50 concurrent logins.\n\n## Scope Decisions\n\n| # | Proposal | Effort | Decision | Reasoning |\n|---|----------|--------|----------|-----------|\n| 1 | \"View all\" link per panel to the existing full page | S | ACCEPTED | In blast radius; full pages already own older-page navigation |\n| 2 | Relative timestamps with absolute time in title/aria-label | S | ACCEPTED | In blast radius; accessibility policy requires named controls |\n| 3 | Skeleton geometry matches final rows (fixed-height, clamped rows, 5 per panel) | S | ACCEPTED | Prevents layout shift on the login-critical page |\n| 4 | Refetch on tab focus (\u226530s interval, no polling, no ETag) + \"Updated \u2026\" header line | S | ACCEPTED (the one accepted expansion beyond the named feature) | Stale alerts on a landing page erode trust; the interval bounds request volume |\n| 5 | Toast pauses on hover/focus, Escape dismisses | S | ACCEPTED | Part of building the toast primitive correctly |\n| 6 | Panel `<section aria-labelledby>` landmarks | S | ACCEPTED | Zero-cost accessibility |\n| 7 | Unread badge in page shell nav | S-M | DEFERRED | Outside blast radius (shell navigation); needs `unreadCount` to be exposed outside the dashboard response |\n| 8 | Undo for mark-all-read (do-then-undo) | M | DEFERRED | Requires an inverse mutation API; v1 introduces no new mutation APIs |\n| 9 | ETag/304 on GET /api/dashboard | S | DEFERRED | Borderline value; the 30s focus-refetch interval bounds the cost it would save |\n| 10 | Baseline instrumentation of the current three-page flow + `source` attribute on action events | S | ACCEPTED (parallel prerequisite) | Uses existing analytics events; must be live before the first redirect step; does not gate coding |\n| 11 | Ship two panels only (drop ActivityFeed), optionally after a redirect-to-assigned-work experiment | \u2014 | OPEN (T1) | Independent reviewer's recommendation; provisional direction keeps the user's stated three panels and treats the cohort rollout as the experiment |\n\n## Accepted Scope (added to this plan)\n- \"View all\" link in each panel\n- Relative timestamps with absolute time in title/aria-label\n- Skeletons sized to fixed-height, clamped final rows (5 per list panel)\n- Refetch on tab focus (\u226530s interval) with an \"Updated \u2026\" header line; prior data kept and \"Couldn't refresh\" shown on refetch failure\n- Toast pause-on-hover/focus and Escape dismissal, max 3 visible (oldest evicted), reduced-motion variant\n- Per-panel section landmarks\n- `unreadCount` in the notifications envelope; `snapshotAt` from the database clock\n- Two rollout flags (`dashboard_route`, `dashboard_redirect`) with `returnTo` precedence\n- `source: 'dashboard' | 'legacy'` attribute on existing action start / completion / permission-error events; baseline instrumentation of the current flow (parallel prerequisite)\n\n## Deferred to TODOS.md\n- Unread count badge in the page shell nav (P2; outside blast radius; needs `unreadCount` exposed outside the dashboard response)\n- Undo for mark-all-read (P3; depends on approval of an inverse mutation API)\n- ETag/304 on the dashboard endpoint (P3)\n\n## Taste decisions \u2014 OPEN, closed by the user at the /autoplan Final Approval Gate\n\nThe /autoplan pipeline auto-decides mechanical questions and surfaces close calls to the user once, at its final gate. These three are provisionally set to the plan's original direction and remain OPEN until the user confirms or overrides there. Sections above marked PROVISIONAL depend on them; everything marked DECIDED or unmarked is settled.\n\n- **T1 (OPEN):** three panels (provisional) vs two panels, optionally after a redirect-to-assigned-work experiment (independent reviewer). Provisional: three, QuickActions first, cohort rollout as the experiment.\n- **T2 (OPEN):** aggregate endpoint with envelopes (provisional) vs three per-panel endpoints (independent reviewer). Provisional: aggregate.\n- **T3 (OPEN):** confirmation modal (provisional) vs do-then-undo (independent reviewer). Provisional: modal, because v1 adds no mutation API.\n\nOwner: the user.\n",
|
|
"viewport": " 140 +4. **DB-clock `snapshotAt` \"same round trip\" touches the repo** \u2014 ADOPTED. `SELECT now()` runs in its own round tr\n +ip immediately before the notifications list query. A notification inserted between the two stays unread (the safe\n + direction). The ordering test inserts between the clock read and the list query as well as between GET and POST. \n 141 +5. **Notifications type requires `snapshotAt`/`unreadCount` on `ok:false`** \u2014 ADOPTED. `notifications: Envelope<{ \n +items: NotificationItem[]; snapshotAt: string; unreadCount: number }>`. `unreadCount` is computed inside the notif\n +ications sub-query and shares its envelope. \n 142 +6. **`dashboard_view` undefined** \u2014 ADOPTED. Emitted once per page mount on the first 200 response from the endpoi\n +nt (regardless of envelope statuses). New events/metrics are explicit work items: `dashboard_view`, `panel_rendere\n +d{panel,state}`, `quick_action_click{actionId}`, `mark_all_read{result}`, `dashboard_request_total{status}`, `dash\n +board_request_ms`, `dashboard_panel_status{panel,status}`, `dashboard_predicate_error_total{actionId}`. \n 143 +7. **`redirect=on, route=off`** \u2014 ADOPTED. The post-login redirect applies only when both flags are on for the mem\n +ber; the route flag alone never redirects. \n 144 +8. **\"Stale `snapshotAt` \u2192 400\" not in the contract** \u2014 ADOPTED. Step 0 gains \"bulk-read rejects malformed or futu\n +re `snapshotAt` with a validation error\"; if it does not, the client guard (refetch then retry once) is the only d\n +efense and the copy stays \"Couldn't mark as read. Reload and try again.\" \n 145 +9. **\"Guardrails flat\" has no threshold** \u2014 ADOPTED. Per step: completed-task rate for the redirect cohort within \n +2 percentage points of the control cohort (overall and for the high-frequency segment); permission-error rate for \n +dashboard-sourced actions no more than 1 percentage point above the legacy-sourced rate for the same actions. \n 146 +10. **Row anatomy, md breakpoint, Escape collision, TimeoutError mapping, unreadCount placement** \u2014 ADOPTED. Notif\n +ication row: unread dot with `aria-label=\"Unread\"`, title (two-line clamp), relative time; panel heading shows \"No\n +tifications (N)\" from `unreadCount`. Activity row: actor, summary (two-line clamp), relative time. Layout is stack\n +ed below `lg`, two-column at `lg` and above. Toast Escape dismisses only when focus is inside the toast region, so\n + it never competes with the dialog's Escape. `runSubQuery` maps the `TimeoutError` DOMException (or the race timeo\n +ut) to `RepoTimeoutError`. \n 147 +11. **Unknown sub-query error \u2192 whole-page 500** \u2014 DECLINED, intentional. An unknown exception class is a bug, not\n + an outage; hiding it as `unavailable` would render a green-looking page over a defect. The 500 carries `requestId\n +`, the page shows a page-level error with Retry, and the alert fires. Trade-off recorded. \n 148 +12. **500-event threshold may be unreachable at 5%** \u2014 ADOPTED. Hold rule: at least 24h and at least 500 `dashboar\n +d_view` events, capped at 7 days; after 7 days proceed if guardrails are within threshold on whatever volume exist\n +s. \n 149 +13. **Toast stacking ahead of need** \u2014 DECLINED. The cap and eviction are a few lines; the primitive is app-root b\n +y design and the plan names a toast *system*. \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",
|
|
"events": [
|
|
{
|
|
"sessionId": "a828fa7a-3d8e-48cd-ae1f-e3a9cf0611b2",
|
|
"timestamp": "2026-09-10T08:12:51.142Z",
|
|
"toolUseId": "toolu_01UB3V8rTihFzkB6i7Hhbe8B",
|
|
"kind": "use",
|
|
"name": "Write",
|
|
"input": {
|
|
"file_path": "/tmp/gstack-paid-shard-tVuvKy/tmp/gstack-hermetic-1626312-olYxKb/skill-home-deJ6wd/.gstack/projects/gstack-autoplan-chain-tcPo1b/ceo-plans/2026-09-10-user-dashboard.md",
|
|
"content": "---\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-tcPo1b (local, no remote)\n\n## Step 0 \u2014 Premise P0: verify the \"existing\" contracts before building\n\nThe repository reviewed contains no application code (only README.md and the plan). Every \"existing\" item below is taken from the plan author's contract section and is unverified. Before any implementation task starts, confirm each exists and behaves as stated:\n\n- [ ] Cookie session + workspace membership middleware; request context exposes `memberId` and `workspaceId`\n- [ ] Activity and notification list repository methods: latest 20 + cursor, indexed on workspace/member + created_at; note whether they accept a `limit` argument and an `AbortSignal`\n- [ ] Existing item types `ActivityItem`, `NotificationItem` (must carry a read flag such as `readAt`), `QuickAction` (id, label, routeTarget) as used by the current activity/notification pages and the action registry\n- [ ] A notifications unread-count read method, or an indexed `(member_id) WHERE read_at IS NULL` path that a one-line COUNT can use\n- [ ] Action registry: 3 actions with stable IDs, labels, route targets, server-side eligibility predicates\n- [ ] **Existing** member-scoped bulk-read mutation (working name `POST /api/notifications/read-all`): idempotent, marks notifications with `created_at \u2264 snapshotAt`, requires CSRF token\n- [ ] Typed HTTP client errors: unauthenticated, forbidden, validation, retryable-service, network\n- [ ] Dialog primitive with focus trap, Escape dismissal, focus return; Tailwind tokens; responsive page shell; motion tokens or a `prefers-reduced-motion` utility\n- [ ] Vitest + React Testing Library + Playwright in CI; fixtures for member, other-workspace, empty lists, service failures\n- [ ] Staging feature flags with member-cohort targeting (two independent boolean flags can be created); request/error metrics\n- [ ] Existing analytics events: login, action start, action completion, permission error; confirm they accept a context/source attribute (needed for `source: 'dashboard'`)\n- [ ] Existing full pages for activity and notifications (targets of the \"View all\" links); existing post-login redirect code and its `returnTo` handling\n\nIf any box fails, the Eng phase reopens the affected decision before implementation. Known fallbacks: if repos do not accept an `AbortSignal`, a thin signal-aware wrapper (`runSubQuery`) enters blast radius and the timeout is a `Promise.race` that abandons the still-running query (accepted: bounded user wait, not bounded DB work). If no unread-count method exists, a single indexed COUNT query is added inside `DashboardService` (a read, no schema change).\n\n## Success criteria\n\nMetric: median time from login to first completed task. Baseline: 75s (team walkthrough, small sample). Target: 45s. Guardrails: completed-task rate must not regress overall or for the high-frequency member segment; permission-error rate for actions started from the dashboard must not exceed the permission-error rate for the same actions started from the current three-page flow (control cohort). Existing action start / completion / permission-error events gain a `source: 'dashboard' | 'legacy'` attribute so both comparisons are possible. Baseline instrumentation of the current three-page flow runs in parallel with the build and must be live before the first redirect step.\n\n## Vision\n\n### 10x Check\nThe login surface that knows what you should do next: assigned work ranked by urgency, alerts that block that work, and a digest of what changed since your last visit, each one a single keypress away. This plan builds the home surface and the two shared primitives (toast, confirm dialog) that version needs; the ranking and digest services are explicitly deferred to the personalization plan.\n\n## Approach \u2014 PROVISIONAL pending T2 (aggregate endpoint) at the Final Gate\n\nOne aggregate `GET /api/dashboard`, no request parameters, scoped from request context only. Three sub-queries run in parallel through `runSubQuery(name, fn, signal)` and `Promise.allSettled`.\n\n```ts\ntype Envelope<T> = { ok: true; data: T } | { ok: false; error: { code: 'timeout' | 'unavailable' } };\ntype DashboardResponse = {\n activity: Envelope<ActivityItem[]>; // 5 items\n notifications: Envelope<NotificationItem[]> & { snapshotAt: string; unreadCount: number }; // 5 items\n quickActions: Envelope<QuickAction[]>; // 0-3 items\n meta: { serverTime: string; requestId: string };\n};\n```\n\nRow limits: each list panel shows 5 rows. The endpoint passes `limit: 5` if the repository method accepts it; otherwise it slices the returned 20 server-side. `unreadCount` is the member's total unread count, not the count within the 5 rows. `snapshotAt` is the database clock (`SELECT now()`) taken in the same round trip as the notifications query, so app-instance clock skew cannot mark or miss notifications incorrectly.\n\nError boundary rule: `Promise.allSettled` never throws, so the handler classifies each settled result explicitly: fulfilled \u2192 `ok:true`; rejected with `RepoTimeoutError` (from the per-sub-query `AbortSignal.timeout`, or the race fallback) \u2192 `ok:false, code:'timeout'`; rejected with `ServiceUnavailableError` \u2192 `ok:false, code:'unavailable'`; rejected with anything else \u2192 rethrown after settlement so the framework returns 500 with `requestId`. The response is HTTP 200 whenever auth and membership pass, even if all three envelopes are `ok:false`. Pre-fan-out failures return non-200: no session \u2192 401, not a member \u2192 403. A throwing eligibility predicate omits that one action, logs a warning with `actionId`, and increments `dashboard_predicate_error_total`.\n\nLatency: p95 budget for the endpoint is \u2264300ms in staging at 50 concurrent logins. The 2000ms per-sub-query cap is a hang ceiling, not a latency target; a sub-query that hits it renders a panel error and `dashboard_panel_status{status=timeout}` alerts. Eng phase may lower the cap to 1000ms on staging evidence.\n\nData fetching: one `useDashboardQuery` hook (plain `fetch` + `AbortController`, no new library) holding `{ data, error, isFetching, updatedAt }`. Retry on any panel re-invokes the hook; because the endpoint is single and parameterless, all three panels re-enter loading together. On tab focus it refetches if at least 30s have passed since `updatedAt`; there is no polling and no ETag in v1. A header line under the page title shows \"Updated just now\" / \"Updated 3m ago\" from `updatedAt`. On refetch failure the last successful data stays, and the header line becomes \"Couldn't refresh\" until the next successful fetch; this is inline text, not a toast, and not the panel error state. A 401 during a focus refetch redirects to login exactly like the initial load.\n\nPanel render states (shared `usePanelState` hook, copy in `dashboardCopy.ts`):\n\n| Panel | Loading | Empty (`ok:true, data:[]`) | Error (`ok:false` or fetch error) | Success |\n|---|---|---|---|---|\n| QuickActions | 3 fixed-height pill skeletons | \"No actions available\" | \"Couldn't load actions\" + Retry | 1-3 action buttons |\n| NotificationsPanel | 5 fixed-height row skeletons | \"You're all caught up\" | \"Couldn't load notifications\" + Retry | 5 rows + \"Mark all as read\" + \"View all\" |\n| ActivityFeed | 5 fixed-height row skeletons | \"No activity yet\" | \"Couldn't load activity\" + Retry | 5 rows + \"View all\" |\n\nRows are fixed height with a two-line clamp and full text in `title`, so skeleton geometry matches final rows. A brand-new member sees three empty states, never a blank page. Panel order is QuickActions \u2192 NotificationsPanel \u2192 ActivityFeed at every breakpoint (stacked on sm; two-column on lg with activity on the right).\n\n### Mark all as read \u2014 PROVISIONAL pending T3 (modal vs undo) at the Final Gate\n\n`ConfirmDialog` (composed on the existing dialog primitive) confirms the **existing** bulk-read mutation, posting the last response's `notifications.snapshotAt` plus the CSRF token. No new mutation API is introduced; that is why undo (which would need an inverse mutation) is deferred. The trigger button is disabled when `unreadCount === 0` and Confirm is disabled while submitting. Cancel and Escape remain available while the request is in flight; the in-flight request is aborted and its result ignored. Failures stay inline in the modal and re-enable Confirm: 401 \u2192 redirect to login; CSRF/403 \u2192 \"Session expired, reload\"; validation/400 (malformed or stale `snapshotAt`) \u2192 \"Couldn't mark as read. Reload and try again.\"; network/5xx \u2192 \"Couldn't mark as read. Try again.\" Success closes the modal, returns focus to the trigger, refetches the dashboard, and shows a success toast.\n\n### Toast (DECIDED)\n\n`ToastProvider` + `useToast` mounted at the app root (an explicit, minimal shared-code touch), `aria-live=\"polite\"`, 5s auto-dismiss paused on hover/focus, Escape and close button dismiss, max 3 visible with the oldest evicted first, reduced-motion variant using the existing motion utility. v1 emitter: mark-all-read success only. It is built as an app-root primitive on purpose: the plan names a \"toast notification system\", no toast code exists today, and mounting it at the root prevents a second ad-hoc toast implementation in the next feature.\n\n### Blast radius (definition used for cherry-pick decisions)\n\nThe new dashboard route, page, panels, hooks, `DashboardService`/handler, `runSubQuery`, the two new primitives (`ToastProvider`, `ConfirmDialog`), their tests, and two explicit minimal touches to shared code: the `ToastProvider` mount at the app root and the post-login redirect hook. Existing repos, existing APIs, the page shell's navigation, and existing pages are outside it.\n\n## Rollout (DECIDED)\n\nTwo member-cohort flags: `dashboard_route` (makes `/dashboard` reachable by URL) and `dashboard_redirect` (sends members there after login). Redirect precedence: an explicit `returnTo`/deep link always wins; the dashboard redirect applies only when there is no return URL. Flag-off behavior: a member currently on `/dashboard` when `dashboard_route` turns off is redirected to the current landing page on next navigation or fetch (the route renders a redirect, not a 404).\n\nDeploy order: (1) endpoint dark (`dashboard_route` off everywhere); (2) `dashboard_route` on for the internal cohort, no redirect; (3) `dashboard_redirect` at 5% \u2192 25% \u2192 100% of members, each step held for at least 24h and at least 500 `dashboard_view` events with guardrails flat. Kill switch and rollback: `dashboard_redirect` off returns members to the current landing page in seconds; `dashboard_route` off removes the page; the endpoint can stay deployed. Post-deploy checklist per step: non-zero `dashboard_request_total`, zero 5xx, all panel statuses ok, one manual keyboard pass; synthetic login \u2192 GET /api/dashboard smoke test.\n\n## Test acceptance (minimum)\n\nEndpoint: 200 with three ok envelopes; 200 with all three failed; unknown error \u2192 500 with requestId; 401 and 403 pre-fan-out; `?workspaceId=` ignored; `unreadCount` and `snapshotAt` present; `limit` 5 honored. Panels: loading / empty / error+Retry / success for each of the three; Retry reloads all. Modal: confirm, cancel, Escape (including in flight), double-click \u2192 one request, 401, csrf/403, validation, network; trigger disabled at `unreadCount` 0; focus returns to trigger. Ordering: notification inserted between GET and POST stays unread. Toast: cap 3 oldest evicted, hover pause, Escape, reduced motion. Hook: 30s focus interval, failure keeps data and shows \"Couldn't refresh\", 401 redirects. E2E: both flags off/on combinations, `returnTo` precedence, keyboard-only modal path, axe per panel state, prefers-reduced-motion. Staging: p95 \u2264300ms at 50 concurrent logins.\n\n## Scope Decisions\n\n| # | Proposal | Effort | Decision | Reasoning |\n|---|----------|--------|----------|-----------|\n| 1 | \"View all\" link per panel to the existing full page | S | ACCEPTED | In blast radius; full pages already own older-page navigation |\n| 2 | Relative timestamps with absolute time in title/aria-label | S | ACCEPTED | In blast radius; accessibility policy requires named controls |\n| 3 | Skeleton geometry matches final rows (fixed-height, clamped rows, 5 per panel) | S | ACCEPTED | Prevents layout shift on the login-critical page |\n| 4 | Refetch on tab focus (\u226530s interval, no polling, no ETag) + \"Updated \u2026\" header line | S | ACCEPTED (the one accepted expansion beyond the named feature) | Stale alerts on a landing page erode trust; the interval bounds request volume |\n| 5 | Toast pauses on hover/focus, Escape dismisses | S | ACCEPTED | Part of building the toast primitive correctly |\n| 6 | Panel `<section aria-labelledby>` landmarks | S | ACCEPTED | Zero-cost accessibility |\n| 7 | Unread badge in page shell nav | S-M | DEFERRED | Outside blast radius (shell navigation); needs `unreadCount` to be exposed outside the dashboard response |\n| 8 | Undo for mark-all-read (do-then-undo) | M | DEFERRED | Requires an inverse mutation API; v1 introduces no new mutation APIs |\n| 9 | ETag/304 on GET /api/dashboard | S | DEFERRED | Borderline value; the 30s focus-refetch interval bounds the cost it would save |\n| 10 | Baseline instrumentation of the current three-page flow + `source` attribute on action events | S | ACCEPTED (parallel prerequisite) | Uses existing analytics events; must be live before the first redirect step; does not gate coding |\n| 11 | Ship two panels only (drop ActivityFeed), optionally after a redirect-to-assigned-work experiment | \u2014 | OPEN (T1) | Independent reviewer's recommendation; provisional direction keeps the user's stated three panels and treats the cohort rollout as the experiment |\n\n## Accepted Scope (added to this plan)\n- \"View all\" link in each panel\n- Relative timestamps with absolute time in title/aria-label\n- Skeletons sized to fixed-height, clamped final rows (5 per list panel)\n- Refetch on tab focus (\u226530s interval) with an \"Updated \u2026\" header line; prior data kept and \"Couldn't refresh\" shown on refetch failure\n- Toast pause-on-hover/focus and Escape dismissal, max 3 visible (oldest evicted), reduced-motion variant\n- Per-panel section landmarks\n- `unreadCount` in the notifications envelope; `snapshotAt` from the database clock\n- Two rollout flags (`dashboard_route`, `dashboard_redirect`) with `returnTo` precedence\n- `source: 'dashboard' | 'legacy'` attribute on existing action start / completion / permission-error events; baseline instrumentation of the current flow (parallel prerequisite)\n\n## Deferred to TODOS.md\n- Unread count badge in the page shell nav (P2; outside blast radius; needs `unreadCount` exposed outside the dashboard response)\n- Undo for mark-all-read (P3; depends on approval of an inverse mutation API)\n- ETag/304 on the dashboard endpoint (P3)\n\n## Taste decisions \u2014 OPEN, closed by the user at the /autoplan Final Approval Gate\n\nThe /autoplan pipeline auto-decides mechanical questions and surfaces close calls to the user once, at its final gate. These three are provisionally set to the plan's original direction and remain OPEN until the user confirms or overrides there. Sections above marked PROVISIONAL depend on them; everything marked DECIDED or unmarked is settled.\n\n- **T1 (OPEN):** three panels (provisional) vs two panels, optionally after a redirect-to-assigned-work experiment (independent reviewer). Provisional: three, QuickActions first, cohort rollout as the experiment.\n- **T2 (OPEN):** aggregate endpoint with envelopes (provisional) vs three per-panel endpoints (independent reviewer). Provisional: aggregate.\n- **T3 (OPEN):** confirmation modal (provisional) vs do-then-undo (independent reviewer). Provisional: modal, because v1 adds no mutation API.\n\nOwner: the user.\n"
|
|
}
|
|
},
|
|
{
|
|
"sessionId": "a828fa7a-3d8e-48cd-ae1f-e3a9cf0611b2",
|
|
"timestamp": "2026-09-10T08:12:54.651Z",
|
|
"toolUseId": "toolu_01UB3V8rTihFzkB6i7Hhbe8B",
|
|
"kind": "result",
|
|
"content": "The file /tmp/gstack-paid-shard-tVuvKy/tmp/gstack-hermetic-1626312-olYxKb/skill-home-deJ6wd/.gstack/projects/gstack-autoplan-chain-tcPo1b/ceo-plans/2026-09-10-user-dashboard.md has been updated successfully. (file state is current in your context \u2014 no need to Read it back)",
|
|
"isError": false
|
|
}
|
|
],
|
|
"reconstructedRequest": {
|
|
"old_string": "Owner: the user.\n",
|
|
"new_string": "Owner: the user.\n\n4. **DB-clock `snapshotAt` \"same round trip\" touches the repo** \u2014 ADOPTED. `SELECT now()` runs in its own round trip immediately before the notifications list query. A notification inserted between the two stays unread (the safe direction). The ordering test inserts between the clock read and the list query as well as between GET and POST.\n5. **Notifications type requires `snapshotAt`/`unreadCount` on `ok:false`** \u2014 ADOPTED. `notifications: Envelope<{ items: NotificationItem[]; snapshotAt: string; unreadCount: number }>`. `unreadCount` is computed inside the notifications sub-query and shares its envelope.\n6. **`dashboard_view` undefined** \u2014 ADOPTED. Emitted once per page mount on the first 200 response from the endpoint (regardless of envelope statuses). New events/metrics are explicit work items: `dashboard_view`, `panel_rendered{panel,state}`, `quick_action_click{actionId}`, `mark_all_read{result}`, `dashboard_request_total{status}`, `dashboard_request_ms`, `dashboard_panel_status{panel,status}`, `dashboard_predicate_error_total{actionId}`.\n7. **`redirect=on, route=off`** \u2014 ADOPTED. The post-login redirect applies only when both flags are on for the member; the route flag alone never redirects.\n8. **\"Stale `snapshotAt` \u2192 400\" not in the contract** \u2014 ADOPTED. Step 0 gains \"bulk-read rejects malformed or future `snapshotAt` with a validation error\"; if it does not, the client guard (refetch then retry once) is the only defense and the copy stays \"Couldn't mark as read. Reload and try again.\"\n9. **\"Guardrails flat\" has no threshold** \u2014 ADOPTED. Per step: completed-task rate for the redirect cohort within 2 percentage points of the control cohort (overall and for the high-frequency segment); permission-error rate for dashboard-sourced actions no more than 1 percentage point above the legacy-sourced rate for the same actions.\n10. **Row anatomy, md breakpoint, Escape collision, TimeoutError mapping, unreadCount placement** \u2014 ADOPTED. Notification row: unread dot with `aria-label=\"Unread\"`, title (two-line clamp), relative time; panel heading shows \"Notifications (N)\" from `unreadCount`. Activity row: actor, summary (two-line clamp), relative time. Layout is stacked below `lg`, two-column at `lg` and above. Toast Escape dismisses only when focus is inside the toast region, so it never competes with the dialog's Escape. `runSubQuery` maps the `TimeoutError` DOMException (or the race timeout) to `RepoTimeoutError`.\n11. **Unknown sub-query error \u2192 whole-page 500** \u2014 DECLINED, intentional. An unknown exception class is a bug, not an outage; hiding it as `unavailable` would render a green-looking page over a defect. The 500 carries `requestId`, the page shows a page-level error with Retry, and the alert fires. Trade-off recorded.\n12. **500-event threshold may be unreachable at 5%** \u2014 ADOPTED. Hold rule: at least 24h and at least 500 `dashboard_view` events, capped at 7 days; after 7 days proceed if guardrails are within threshold on whatever volume exists.\n13. **Toast stacking ahead of need** \u2014 DECLINED. The cap and eviction are a few lines; the primitive is app-root by design and the plan names a toast *system*.\n"
|
|
}
|
|
}
|