Context. surface customer · domain discovery · route /activity · auth signed-in · source apps/oshun/web/src/app/activity/page.tsx
Last walked. 2026-06-27 automated coverage reconciliation by Codex — real-dev Activity Playwright expanded for scoped empty live timeline, live loading, real Nisaba notebook write/read through BFF activity aggregation, support-lane absence notes, focal shortcuts, desktop/laptop support layouts; component, telemetry, shell-state, outage, PWA/offline, accessibility, and BFF contract coverage cross-checked.
Purpose#
A cross-domain continuity surface. Activity surfaces the real threads worth returning to — saved continuations, notebooks, tutoring/session artifacts, assistant excerpts, and backed achievement progress — grouped into "continuity threads" so the next move is easy to pick up. Milestones, streak calendar, and weekly digest have UI components but no live V1 backing projection yet; the route renders explicit unavailable notes instead of fixture data.
Entry points#
- Shell nav: Activity tab —
WEB_SHELL_ROUTE_PATHS.activity; primary entry from any shell route - Home rail ("See all" on the activity feed on
/) — seehome.md - Home focal-stage shortcut —
Back to HomeandOpen Libraryrail shortcuts inside the Activity focal stage cross-link from sibling shell surfaces (so users return here from there) - Re-entry from messages, library, or domain surfaces when an action card
deep-links to
/activity - Direct URL / bookmark — yes, auth required (no anonymous redirect logic in
page.tsx; relies on shell middleware) - Shell state preview —
?shellState=<mode>swaps the content forShellSurfaceStatePreview(loading / empty / error / offline / etc.) viaresolveShellRouteSurfaceContent({ surface: 'activity' })
Layout regions#
page.tsx mounts ShellLayout with active="activity" and breadcrumbs
Home → Activity, then renders <ActivityDashboard /> (or the shell-state
preview).
Inside ActivityDashboard:
- Focal stage (
ShellRouteAnchor+ShellLandingFocalStage):- Focal canvas headline ("N threads are ready to reopen right now."), summary,
and up to two "lead thread" cards (
data-activity-focal-thread) - Focal rail with
DataSourceIndicator(Live data / Offline data, refresh) plus metric pills (Threads, Cross-domain, Domains, Ready now) - Useful next surfaces card (Back to Home, Open Library)
- Focal canvas headline ("N threads are ready to reopen right now."), summary,
and up to two "lead thread" cards (
- Primary lane —
EnhancedTimeline:- Section header "Continuity timeline" with summary line
N threads · N cross-domain · N ready to resumeand "Mark all read" affordance - Continuity summary tile grid (
data-activity-continuity-stat) WorkspaceToolbar("Activity controls") with domainWorkspaceSegmentRailand thread-type chip pills, plus aReset filterschip when filters are on- Timeline list with a vertical guideline; time-grouped continuity thread cards
- Section header "Continuity timeline" with summary line
- Support lane (
WorkspaceSupportLane):AchievementsSection— achievement card grid with rarity borders + detail overlay when/v1/achievements/user+/definitionsreturn recordsMilestonesSection— milestone cards with confetti celebration when data is supplied; live route currently shows an unavailable noteStreakCalendarSection— per-day streak calendar with domain dots when data is supplied; live route currently shows an unavailable noteWeeklyDigestSection— glass-style weekly review panel with expandable highlights when data is supplied; live route currently shows an unavailable note- Docked aside on wide desktop (
supportDocked); collapses into a single lane below the timeline on smaller breakpoints
States#
- Initial live fetch / loading —
useActivityData()startsloading=true;DataSourceIndicatorintentionally returns null while the shell, focal stage, and timeline stay composed. Covered byactivity-cross-domain-continuity.spec.tswith a delayed real/v1/activityrequest. - Live data, populated —
isLive=true; live BFF activity timeline renders a real seeded Nisaba notebook in the focal lead card and the timeline. Covered byactivity-cross-domain-continuity.spec.tsand BFF route tests for real Nisaba/Metis records. - Honest empty live timeline — scoped real BFF read with no Nisaba/Metis
records shows "Nothing is waiting to reopen right now.", no invented
continuity threads, no retired
thread-grounding-evidence, anddata-activity-empty. Covered byactivity-cross-domain-continuity.spec.ts. - Offline / fallback / endpoint failure — live endpoint failure renders
CustomerShellOutageBanner("Live activity is unavailable") and keeps activity sections honest-empty instead of showing fixture fallback data. Covered byshell-partial-outage-banners.spec.tsandActivityDashboard.test.tsx. - Error (recoverable) —
errortext is surfaced in the data-source indicator and outage banner reason; Refresh retriesuseActivityData().refresh(). Covered by component tests and the route-level outage Playwright spec. - Empty filter result — timeline shows "No continuity threads match the
current filters." when active domain/kind filters remove all threads.
Covered by
activity-cross-domain-continuity.spec.tsand component filter tests. - No unread items — "Mark all read" is hidden when
unreadCount=0; read cards have no unread dot. Covered by the empty Activity E2E andActivityDashboard.test.tsx. - Active filters — domain
!== 'all'or kind!== 'all'revealsReset filterschip. Covered byActivityDashboard.test.tsx. - Achievement detail overlay — clicking an achievement opens the
modal-style
AchievementDetailOverlay; close button hides it. Covered byActivityDashboard.test.tsx. - Unavailable live support projections — milestone, streak-calendar, and
weekly-digest backing projections are absent in V1; the live route shows
unavailable notes for each. Covered by
activity-cross-domain-continuity.spec.ts. - Milestone celebration —
MilestonesSectiontriggers a confetti burst viasetCelebrating(id)for ~2.5 s when milestone data is supplied. Covered byActivityDashboard.test.tsxfixture coverage. - Reduced motion —
disableAnimationsuppresses entrance animations, confetti, pulses, and spin in component coverage; shell PWA reduced-motion spinner behavior is covered inpwa-lifecycle-deepening.spec.ts. - Shell state preview —
?shellState=loading|empty|error|offline|...replaces the workspace withShellSurfaceStatePreview(handled inpage.tsx). Covered byshell-surface-states.spec.ts. - Standalone PWA — Activity relies on shell PWA behavior; standalone
cold start and offline cached shell restore
/activitywithout a route-specific branch. Covered bypwa-lifecycle-deepening.spec.tsandoffline-shell-routes.spec.ts.
Interactions#
Focal stage#
- Lead thread card (link,
data-activity-focal-thread)- Function: navigates to
thread.actionHreforWEB_SHELL_ROUTE_PATHS.activity - Mobile: wraps; domains chip-bar reflows
- Telemetry: none in this branch.
- Coverage: real Nisaba notebook focal lead href asserted in
activity-cross-domain-continuity.spec.ts; component layout covered inActivityDashboard.test.tsx.
- Function: navigates to
-
Refresh(button insideDataSourceIndicator)- Function: calls
useActivityData().refresh(); re-fetches the activity payload - Disabled when: none — always clickable; loading branch returns null entirely
- Coverage:
ActivityDashboard.test.tsx.
- Function: calls
-
Back to Home/Open Libraryshortcut cards- Function: navigate to
WEB_SHELL_ROUTE_PATHS.home/WEB_SHELL_ROUTE_PATHS.library - Coverage: route hrefs asserted in
activity-cross-domain-continuity.spec.ts.
- Function: navigate to
Continuity timeline header#
-
Mark all read(button)- Function: optimistically marks every timeline item read after a 400 ms delay; spinner shows during the delay
- Disabled when:
markingAllRead === true(cursorwait, 0.6 opacity) - Hidden when:
unreadCount === 0 - Coverage: spinner + all-read transition covered in
ActivityDashboard.test.tsx; hidden state covered inactivity-cross-domain-continuity.spec.ts.
Activity controls toolbar#
- Domain segments (
DomainFilterTabs,WorkspaceSegmentRail)- Function: filters threads by
WebNavigableDomainId | 'all' - Aria-label: "Activity domains"
- Options: All, Tara, Veritas, Nyx, Arete, Nisaba, Metis
- Coverage: route empty filters in
activity-cross-domain-continuity.spec.ts; component Tara/Metis filtering inActivityDashboard.test.tsx.
- Function: filters threads by
- Thread type pills (
KindFilterPills,WorkspaceChoiceChip)- Function: filters by
ActivityKind | 'all' - Options: All types, update, reminder, event, insight
- Coverage: route kind-empty filter in
activity-cross-domain-continuity.spec.ts; component kind filtering inActivityDashboard.test.tsx.
- Function: filters by
-
Reset filters(button) — appears only when filters are active; resets to All / All -
<N> threadsstatus badge (WorkspaceStatusBadge) — non-interactive
Continuity thread card (one per thread; nested step rows)#
- Card hover/focus — translates 2 px upward, soft drop-shadow
- Unread dot — pulses; suppressed when
disableAnimationorunreadCount === 0 - Domain path label, step count, status label, priority badge, timestamp — non-interactive eyebrows
- Thread reason and metrics row — non-interactive
- Domain chips — non-interactive (one per
thread.domains) - Step row · action link (Link,
data-activity-thread-step-action)- Function: navigates to
step.actionHref - Telemetry:
trackActivityReentryOpenedwithtrigger: 'step'
- Function: navigates to
- Step row · Mark read (button) — visible only on unread steps; calls
handleMarkRead(step.id) - Step assistant highlight link — Telemetry:
trackActivityReentryOpenedwithtrigger: 'assistant-highlight' - Step study-continuation link (notebook / course / assessment /
tutoring) — Telemetry:
trackActivityReentryOpenedwithtrigger: 'study-continuation' - Thread action link (e.g., "Resume") — Telemetry:
trackActivityReentryOpenedwithtrigger: 'thread'- Coverage: thread/card rendering and mark-read interactions in
ActivityDashboard.test.tsx; thread/step/assistant/study telemetry inActivityDashboardTelemetry.test.tsxandactivity-reentry-analytics.spec.ts; real notebook action href and click inactivity-cross-domain-continuity.spec.ts.
- Coverage: thread/card rendering and mark-read interactions in
Achievements section#
- Achievement card (button) — opens
AchievementDetailOverlay - Achievement detail overlay
- Close (×) — closes overlay
- Backdrop click — closes overlay
- Lists Requirements and Tips when present
- Shows progress bar when locked; "Unlocked on …" when unlocked
- Coverage:
ActivityDashboard.test.tsx.
Milestones section#
- Milestone celebrate trigger — fires
ConfettiBurstfor ~2.5 s when milestone data is supplied. Covered byActivityDashboard.test.tsx.
Streak calendar#
- Per-day cell — entrance staggered; reduced motion suppresses animations
- Domain dots inside cells — non-interactive indicators
- Coverage:
ActivityDashboard.test.tsx.
- Coverage:
Weekly digest#
- Highlights expand/collapse — expanding panel inside
glass-styled section- Coverage:
ActivityDashboard.test.tsx.
- Coverage:
Data & contracts#
- Reads:
useActivityData()(seeapps/oshun/web/src/components/activity/data/useActivityData.ts) fans out to live backends:- Timeline:
GET /v1/activity(BFF alias for/activity), returning only real member activity from authorized domains. - Achievements:
GET /v1/achievements/userplusGET /v1/achievements/definitions. - Milestones, streak calendar, and weekly digest: no live V1 backing projection yet; the hook leaves these empty/null.
- Timeline:
- Writes: none from
page.tsx;Mark read/Mark all readare local state mutations on theitemslist - Realtime: None observed in this surface — refresh is manual.
- Caching:
useActivityDatastores client state; BFF activity responses use authenticated cache keys partitioned by user, scopes, tenant/residency, route, pagination, tier, and suspended-domain headers. - Auth/role check:
page.tsxhas no auth guard; depends on shell middleware and the BFF bearer scopes (domain:*ordomain:<id>) for Activity aggregation - Telemetry:
trackActivityReentryOpenedfrom@/analytics/activityReentryTelemetry(surfacetimeline, triggersthread | step | study-continuation | assistant-highlight)
Automated coverage#
apps/oshun/web/e2e/activity-cross-domain-continuity.spec.ts— real-dev Activity route coverage for scoped empty live timeline, live loading, real Nisaba notebook write/read through BFF aggregation, focal lead card, thread action navigation, shortcut hrefs, filter-empty states, Metis tab, desktop docked support lane, unavailable live support projections, and laptop collapsed support lane.apps/oshun/web/e2e/activity-reentry-analytics.spec.ts— route-level re-entry analytics for thread, step, assistant-highlight, and study-continuation triggers with mocked contract-shaped timeline data.apps/oshun/bff/src/__tests__/activity-route.test.ts— BFF activity contract: real authorized-domain timelines, no fabricated rows for empty domains, degraded adapter handling, domain-scope filtering, real Nisaba notebook, real Metis ingest/tutoring activity, legacy/v1/activityalias, pagination, cache hits, auth required, and missing-domain-scope rejection.apps/oshun/web/src/components/activity/__tests__/ActivityDashboard.test.tsx— component coverage for dashboard composition, fallback banner, refresh, timeline cards, filtering, mark-read, mark-all-read, achievements overlay, milestone celebration, streak calendar, weekly digest, reduced animation, and support-lane responsive behavior.apps/oshun/web/src/components/activity/__tests__/ActivityDashboardTelemetry.test.tsx— telemetry payload coverage for thread, step, assistant-highlight, and study-continuation re-entry with active filters.- Shared shell/PWA/viewport/a11y coverage:
shell-partial-outage-banners.spec.ts,shell-surface-states.spec.ts,pwa-lifecycle-deepening.spec.ts,offline-shell-routes.spec.ts,viewport-fit.spec.ts,redesigned-surface-breakpoints.spec.ts, andshell-route-accessibility.spec.ts.
Cross-references#
- Shell:
shell/01-app-shell.md - Sibling routes:
home.md— activity feed entry raillibrary.md— focal-rail shortcut destinationmessages.mdexplore.mdsearch.mdswitcher.md
- Component sources:
apps/oshun/web/src/components/activity/ActivityDashboard.tsxapps/oshun/web/src/components/activity/activityContinuityModels.tsapps/oshun/web/src/components/activity/data/useActivityData.tsapps/oshun/web/src/analytics/activityReentryTelemetry.ts
- Feature spec:
V1/features.md - Architecture:
V1/ARCHITECTURE.md
Open questions / known gaps#
- Runtime walk (2026-05-29) — defect FIXED & verified: React 418
hydration mismatch resolved (time-derived values now deferred to a
post-mount effect via
useClientNow/useIsMountedfrom@/hooks/useClientTime; for/coordinatesthe missedRiseSetCalculatorsite was gated). Re-walked against a fresh build: 0 page errors, 0 console errors, HTTP 200. React error #418; visit https://react.dev/errors/418?a. React error 418 is a hydration mismatch — server-rendered HTML differs from the client (this view renders time/position-dependent content, e.g.new Date()/toLocale*, withoutsuppressHydrationWarning). Fix: compute time-dependent values in an effect or gate withsuppressHydrationWarning. Status keptdrafteduntil fixed. - Confirm the real endpoint(s) feeding
useActivityData()and whether the "Live data" badge corresponds to a specific BFF route: timeline is/v1/activity; achievements fan out to/v1/achievements/userand/v1/achievements/definitions;isLiveis true when either live section resolves. - Document write-side semantics:
Mark read/Mark all readare currently client-only state transitions, not BFF writes. - Confirm whether achievement, milestone, streak, and weekly-digest sources
are part of the same
useActivityData()payload or fan-out separately: achievements fan out separately; milestone, streak, and weekly digest have no live V1 backing projection and remain empty/null. - Verify that the focal-stage
Back to Home/Open Librarycards meet touch-target rules on mobile. Covered byactivity-cross-domain-continuity.spec.tsat 390 px width.