---
phase: 18-app-shell-navigation
verified: 2026-05-17T00:00:00Z
status: human_needed
score: 8/9 must-haves verified
overrides_applied: 0
human_verification:
  - test: "Visit the dashboard at http://localhost:8080 and inspect the sidebar"
    expected: "Sidebar shows 'General' section label above 5 nav items (Home, My Tasks, Projects, Events, Team Members), 'Projects' section label above tablo list, and collapse button that shrinks sidebar to icon-only on click"
    why_human: "Visual Figma match requires rendering the browser; CSS collapse behavior is JavaScript-driven and cannot be verified without a live browser"
  - test: "Click the avatar circle in the top header bar"
    expected: "Dropdown opens showing user email, a Settings link, and a red Log out button; clicking outside closes it"
    why_human: "Native details/summary open/close behavior and outside-click JS listener require live browser interaction"
  - test: "Click Log out in the avatar dropdown"
    expected: "POST /logout is submitted with CSRF token, session is destroyed, user is redirected to login"
    why_human: "End-to-end logout flow requires a running server with an active session"
  - test: "Navigate to a tablo detail page"
    expected: "Breadcrumb reads 'Dashboard > [Tablo Title]', not just 'Dashboard'"
    why_human: "Requires live server and a real tablo in the database to verify dynamic breadcrumb rendering"
  - test: "Navigate to /settings"
    expected: "Returns 200 with 'Coming soon' stub page and no 500 error"
    why_human: "Requires a running server to verify the inline handler response"
---

# Phase 18: App Shell & Navigation Verification Report

**Phase Goal:** Redesign the sidebar and top header bar to match the Figma design.
**Verified:** 2026-05-17
**Status:** human_needed
**Re-verification:** No — initial verification

## Goal Achievement

### Observable Truths

| # | Truth | Status | Evidence |
|---|-------|--------|----------|
| 1 | The Go codebase compiles with zero errors after AppLayout signature is extended | VERIFIED | `go build ./...` exits 0 with no output |
| 2 | Every authenticated page still renders (no 500 errors) | UNCERTAIN (human) | Build passes; runtime smoke test requires live server |
| 3 | go test ./... passes with no failures | VERIFIED | All packages pass: internal/auth, internal/db, internal/files, internal/jobs, internal/web, internal/web/ui, templates |
| 4 | Sidebar shows a GENERAL section label above Home, My Tasks, Projects, Events, Team Members nav items | VERIFIED | `DashboardSidebar` in `app_layout.templ` lines 151-157: `sidebar-section-label` div with text "General", `sidebarPrimaryNavItems` returns exactly 5 items |
| 5 | Sidebar shows a PROJECTS section label above the tablo list | VERIFIED | `app_layout.templ` lines 161-177: inner `sidebar-section-label` div with text "Projects" above tablo loop |
| 6 | Chat and Files nav items are removed from the sidebar | VERIFIED | `sidebarPrimaryNavItems` in `app_layout_helpers.go` returns only: Home, My Tasks, Projects, Events, Team Members — no Chat or Files |
| 7 | Clicking the collapse button toggles the sidebar between full-width and icon-only states | UNCERTAIN (human) | Collapse button has correct inline `onclick` JS toggling `sidebar-is-collapsed` on `.dashboard-shell`; `tailwind.css` contains 5 occurrences of `sidebar-is-collapsed` CSS rules — requires browser to verify visual behavior |
| 8 | Every authenticated page shows a top header bar with breadcrumb, search placeholder, and right-side icons | VERIFIED | `PageHeader` component exists in `app_layout.templ` lines 186-267; called from `AppLayout` line 290 inside `.dashboard-main` before `{ children... }` |
| 9 | Breadcrumb renders the correct path for each page | VERIFIED | All 10 handler call sites in handlers_tablos.go, handlers_planning.go, handlers_account.go, handlers_discussion.go, handlers_files.go, handlers_events.go pass `[]templates.BreadcrumbItem` with correct labels and hrefs |
| 10 | Clicking the avatar circle opens a dropdown with workspace info, settings link, and logout button | UNCERTAIN (human) | `details.header-avatar-menu` with `summary` and dropdown div confirmed in `app_layout.templ` lines 237-257; outside-click JS listener at lines 298-302; requires live browser |
| 11 | Logout from the avatar dropdown still works — POST /logout succeeds | UNCERTAIN (human) | Logout `<form method="POST" action="/logout">` with `@ui.CSRFField(csrfToken)` is present in `PageHeader` (line 252-255); requires live server to verify end-to-end |
| 12 | Settings link in dropdown navigates to /settings which renders a stub page | VERIFIED | `/settings` route found at `router.go:90`; inline stub handler returning HTML with "Coming soon" content |
| 13 | go test ./... passes including updated sidebar test and new header test | VERIFIED | All packages pass; `TestTablosDashboard_Header` function confirmed at `handlers_tablos_test.go:647`; assertions for `page-header`, `header-avatar-menu`, `Dashboard`, `header-search-placeholder` all present |

**Score:** 8/9 automated truths verified (4 require human browser/server testing)

### Required Artifacts

| Artifact | Expected | Status | Details |
|----------|----------|--------|---------|
| `backend/templates/app_layout_helpers.go` | BreadcrumbItem struct export | VERIFIED | Lines 6-10: `type BreadcrumbItem struct { Label string; Href string }` |
| `backend/templates/app_layout_helpers.go` | sidebarPrimaryNavItems with Figma-spec items | VERIFIED | Lines 55-63: 5 items, "My Tasks" and "Team Members" present, Chat/Files absent |
| `backend/templates/app_layout.templ` | Extended AppLayout signature (8 params) | VERIFIED | Line 277: `templ AppLayout(title string, user *auth.User, csrfToken string, activePath string, tablos []sqlc.Tablo, pageTitle string, breadcrumb []BreadcrumbItem, headerActions templ.Component)` |
| `backend/templates/app_layout.templ` | DashboardSidebar with GENERAL/PROJECTS sections and collapse button | VERIFIED | Lines 128-181: sections present, inline onclick JS on collapse button at lines 139-147 |
| `backend/templates/app_layout.templ` | PageHeader component with three zones and avatar dropdown | VERIFIED | Lines 186-267: breadcrumb left, search center, bell/inbox/avatar right, details/summary dropdown |
| `backend/internal/web/ui/app.css` | is-collapsed CSS rules | VERIFIED | Lines 333-358: `.dashboard-shell.sidebar-is-collapsed` rules covering grid-template-columns, label hiding, icon centering, project-list hiding, chevron flip |
| `backend/internal/web/ui/app.css` | page-header CSS block | VERIFIED | Lines 362-567: complete `.page-header`, `.breadcrumb`, `.header-avatar-menu`, `.header-avatar-dropdown` CSS |
| `backend/internal/web/handlers_tablos_test.go` | TestTablosDashboard_Header test | VERIFIED | Lines 647-683: function exists with assertions for page-header, header-avatar-menu, Dashboard, header-search-placeholder |
| `backend/internal/web/router.go` | /settings stub route | VERIFIED | Line 90: `r.Get("/settings", ...)` inline handler |

### Key Link Verification

| From | To | Via | Status | Details |
|------|----|-----|--------|---------|
| `handlers_tablos.go` | `templates/tablos.templ` | `TablosDashboard / TabloDetailPage` with `BreadcrumbItem` args | VERIFIED | Grep confirms `[]templates.BreadcrumbItem{...}` at lines 62, 232, 350, 480 |
| `app_layout_helpers.go` | `app_layout.templ` | `sidebarPrimaryNavItems` called in `DashboardSidebar` | VERIFIED | `app_layout.templ` line 153: `for _, item := range sidebarPrimaryNavItems(activePath)` |
| `app_layout.templ` | `app.css` | `sidebar-is-collapsed` toggled by inline JS, CSS rule picks it up | VERIFIED | Both sides confirmed; CSS in app.css and tailwind.css (5 occurrences) |
| `app_layout.templ` | `app.css` | `.page-header` class rendered in `PageHeader`, CSS defines it | VERIFIED | `tailwind.css` has 5 occurrences of `page-header`; 3 occurrences of `header-avatar-menu` |
| `PageHeader` | `AppLayout` | `@PageHeader(...)` called inside `dashboard-main` | VERIFIED | `app_layout.templ` line 290: `@PageHeader(pageTitle, breadcrumb, headerActions, user, csrfToken)` |
| `app_layout.templ` | `/logout` route | `form method=POST action=/logout` with `@ui.CSRFField` | VERIFIED | `app_layout.templ` lines 252-255: form present with CSRF field |
| `handlers_planning.go` | `templates/planning.templ` | `PlanningPage` with breadcrumb args | VERIFIED | `handlers_planning.go:70` contains `[]templates.BreadcrumbItem{{Label: "Planning", Href: ""}}` |
| `handlers_account.go` | `templates/account_providers.templ` | `AccountProvidersPage` with breadcrumb args | VERIFIED | `handlers_account.go:51` contains `[]templates.BreadcrumbItem{{Label: "Linked Providers", Href: ""}}` |
| `handlers_discussion.go` | `templates/tablos.templ` | `TabloDetailPage` with breadcrumb args | VERIFIED | `handlers_discussion.go:82` contains `[]templates.BreadcrumbItem{{Label: "Dashboard", Href: "/"}, {Label: tablo.Title, Href: ""}}` |
| `handlers_files.go` | `templates/tablos.templ` | `TabloDetailPage` with breadcrumb args (2 call sites) | VERIFIED | `handlers_files.go:110,146` both contain breadcrumb args |
| `handlers_events.go` | `templates/tablos.templ` | `TabloDetailPage` with breadcrumb args | VERIFIED | `handlers_events.go:181` contains breadcrumb args |

### Requirements Coverage

| Requirement | Source Plan | Description | Status | Evidence |
|-------------|-------------|-------------|--------|----------|
| NAV-01 | 18-01, 18-02, 18-03 | User sees a redesigned sidebar/nav bar matching Figma (brand section, icon nav items, tablo list section, user footer) | VERIFIED (code) / NEEDS HUMAN (visual match) | `DashboardSidebar` rebuilt with two-section structure; brand/logo at lines 133-147; nav items at 153-157; tablo list at 164-177. SidebarOrganizationFooter moved to avatar dropdown in header — user info accessible via header not sidebar footer. Visual Figma match requires human. |
| NAV-02 | 18-01, 18-03 | User sees a per-page top header bar with page title and contextual action buttons matching Figma | VERIFIED (code) / NEEDS HUMAN (visual match) | `PageHeader` component at `app_layout.templ:186-267`; breadcrumb wired from all 10 handler call sites; `headerActions` slot present with nil-guard |

### Anti-Patterns Found

| File | Line | Pattern | Severity | Impact |
|------|------|---------|----------|--------|
| `backend/templates/app_layout.templ` | 109-122 | `SidebarOrganizationFooter` component defined but never called | Info | Dead code — intentional per Plan 02/03 decisions; safe to remove in a cleanup pass. Not a blocker. |
| `backend/templates/app_layout.templ` | 86-105 | `SidebarProjectsSection` component defined but never called | Info | Dead code — inlined directly into `DashboardSidebar` per Plan 02; safe to remove in cleanup. Not a blocker. |

No TBD, FIXME, or XXX markers found in phase-modified files.

### Human Verification Required

#### 1. Sidebar Visual Figma Match

**Test:** Start the server (`cd backend && go run . serve`), navigate to `http://localhost:8080`, inspect the sidebar.
**Expected:** Sidebar shows brand/logo section at top, "General" section label above 5 nav items (Home, My Tasks, Projects, Events, Team Members), "Projects" section label above the tablo list. Clicking the collapse button shrinks the sidebar to icon-only (~4rem) width and hides all text labels.
**Why human:** CSS collapse behavior is JavaScript-driven; the visual Figma match cannot be verified programmatically.

#### 2. Avatar Dropdown Interaction

**Test:** Click the user avatar circle in the top right of the header bar.
**Expected:** A dropdown opens showing the user email at the top, a "Settings" link, and a red "Log out" button. Clicking outside the dropdown closes it.
**Why human:** Native `<details>/<summary>` open/close behavior and the outside-click JS listener require live browser interaction.

#### 3. Logout Flow from Avatar Dropdown

**Test:** Click "Log out" in the avatar dropdown while authenticated.
**Expected:** A POST request is sent to `/logout` with a valid CSRF token, the session is destroyed, and the user is redirected to the login page.
**Why human:** Requires a running server with an active authenticated session.

#### 4. Tablo Detail Breadcrumb

**Test:** Navigate to a tablo detail page (`/tablos/{id}`).
**Expected:** The breadcrumb in the top header shows "Dashboard > [Tablo Title]" where "Dashboard" is a clickable link back to `/` and "[Tablo Title]" is plain text.
**Why human:** Requires a live server and a real tablo in the database to verify dynamic breadcrumb rendering.

#### 5. /settings Stub

**Test:** Navigate to `/settings` (authenticated).
**Expected:** Returns a 200 response with "Coming soon" text and a back link to the dashboard. No 500 error.
**Why human:** Requires a running server; the handler is an inline `http.HandlerFunc`, not a full template-rendered page.

### Gaps Summary

No gaps blocking goal achievement. All automated checks passed:
- `go build ./...` succeeds with zero errors
- `go test ./...` passes across all packages  
- All 10 handler call sites supply `BreadcrumbItem` args
- `PageHeader` component exists and is wired into `AppLayout`
- `/settings` route is registered
- `TestTablosDashboard_Header` test exists with all required assertions
- Collapsed sidebar CSS rules are in both `app.css` and compiled `tailwind.css`

The 5 human verification items are runtime/visual checks that cannot be validated by static analysis alone. They are expected outcomes of the implementation — the code wiring is complete. Deferred dead code (`SidebarOrganizationFooter`, `SidebarProjectsSection` unused components) is informational only and does not block the phase goal.

---

_Verified: 2026-05-17_
_Verifier: Claude (gsd-verifier)_
