diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md
index f647251..b839871 100644
--- a/.planning/REQUIREMENTS.md
+++ b/.planning/REQUIREMENTS.md
@@ -1,254 +1,57 @@
-# Requirements: Pulse Mobile Shell Redesign
+# Requirements: PAX8 Integration (v2.0)
-**Defined:** 2026-05-03
-**Core Value:** A manager can open Pulse on their phone and, in under 30 seconds, see the state of the business and triage tickets — without ever needing to switch to desktop for read-only awareness.
-**Source spec:** `docs/superpowers/specs/2026-05-03-mobile-shell-design.md`
+**Defined:** 2026-07-10
+**Core Value:** Managers can see PAX8 subscription costs and seat counts alongside existing Autotask company data in Pulse, without manually cross-referencing PAX8's own portal.
+**Source:** `.planning/seeds/SEED-002-pax8-integration.md` (scoped via `/gsd-explore` on 2026-07-10)
-## v1 Requirements
+## v2.0 Requirements
-Requirements for this milestone. Each maps to a spec section and a roadmap phase.
+Requirements for this milestone. Each maps to a roadmap phase.
-### PWA — Progressive Web App scaffolding (spec §4)
+### PAX8 — Client & Auth
-- [ ] **PWA-01**: `public/manifest.json` exists with name "Pulse", short_name "Pulse", `display: "standalone"`, `start_url: "/mobile"`, theme/background colors matching dark and light shells
-- [ ] **PWA-02**: Manifest is referenced from `app/layout.tsx` via ``
-- [ ] **PWA-03**: Viewport meta in `app/layout.tsx` includes `viewport-fit=cover`
-- [ ] **PWA-04**: Header and bottom tab bar respect `env(safe-area-inset-top)` and `env(safe-area-inset-bottom)` (Tailwind arbitrary values or shared utility class)
+- [ ] **PAX8-01**: Pulse authenticates to the PAX8 REST API (`api.pax8.com/v1`) via OAuth2 client-credentials, using the developer-provisioned client ID/secret
+- [ ] **PAX8-02**: `isPax8Configured()` helper reports whether PAX8 credentials are present, following the existing `isConfigured()` factory pattern (`lib/services/pax8-factory.ts`)
-### Shell — `/mobile` layout (spec §5)
+### PAX8 — Data Sync
-- [x] **SHELL-01**: New `app/mobile/layout.tsx` replaces the current layout (rebuild in place — no parallel `/mobile-v2`)
-- [ ] **SHELL-02**: Sticky top header: `bg-background/95 backdrop-blur` + bottom border; left = Wulf mark + "Pulse" wordmark linked to `/mobile/dashboard`; no page title in header
-- [ ] **SHELL-03**: Header right slot — `Bell` icon button (placeholder, no menu/badge, `aria-label="Notifications"`, empty `onClick`, keyboard-accessible)
-- [ ] **SHELL-04**: Header right slot — compact user avatar (`h-7 w-7`); tapping opens the More drawer
-- [x] **SHELL-05**: `` content area is scrollable with bottom padding equal to bottom-nav height + safe-area inset
-- [ ] **SHELL-06**: Fixed bottom nav: `border-t bg-background`, `max-w-lg mx-auto` wrapper, 5 cells (4 tabs + More)
+- [ ] **PAX8-03**: Pulse syncs PAX8 companies into Postgres
+- [ ] **PAX8-04**: Pulse syncs PAX8 subscriptions (product, seat count, billing term) into Postgres
+- [ ] **PAX8-05**: Pulse syncs PAX8 product catalog (SKUs, categories) into Postgres, so subscriptions are human-readable
+- [ ] **PAX8-06**: Pulse syncs PAX8 orders/invoices (historical line items) into Postgres, enabling cost reconciliation over time, not just current-state seats
+- [ ] **PAX8-07**: Sync runs on a daily schedule via the existing `sync-scheduler.ts` cron pattern
+- [ ] **PAX8-08**: All PAX8 sync operations are read-only — no writes back to the PAX8 API (no seat adjustments, no order placement)
+- [ ] **PAX8-09**: PAX8 integration can be toggled on/off via `/admin/integrations`, consistent with other integrations (`integration_settings` table)
-### NAV — Bottom tab bar (spec §3.1)
+### PAX8 — Company Matching
-- [ ] **NAV-01**: Four equal-width primary tabs: Dashboard (`LayoutDashboard`), Tickets (`Ticket`), Finance (`DollarSign`), Analyzer (`Sparkles`)
-- [ ] **NAV-02**: Tabs route to `/mobile/dashboard`, `/mobile/tickets`, `/mobile/finance`, `/mobile/analyzer`
-- [ ] **NAV-03**: Active state uses `text-primary`, inactive uses `text-muted-foreground`; active detection via `pathname.startsWith(href)`
+- [ ] **PAX8-10**: PAX8 companies are automatically matched to existing Autotask companies by fuzzy name similarity at sync time
+- [ ] **PAX8-11**: Unmatched or ambiguous company matches are flagged rather than silently guessed
+- [ ] **PAX8-12**: An admin can view flagged/ambiguous company matches and manually resolve them to the correct Autotask company
-### DRAWER — More Sheet drawer (spec §3.2)
+### PAX8 — UI Surface
-- [ ] **DRAWER-01**: Fifth bottom-bar control labeled "More" with `Menu` icon opens a shadcn `Sheet`
-- [ ] **DRAWER-02**: Sheet uses a single consistent side (`right` or `bottom`) — pick one and stay consistent
-- [ ] **DRAWER-03**: Drawer top section "Mobile sections" lists Engagement (`/mobile/engagement`)
-- [ ] **DRAWER-04**: Drawer middle section "Full site" lists desktop-only pages (Quotes, Configuration Items, Backup Status, Ticket Digest, Admin / Sync) each with `ExternalLink` icon
-- [ ] **DRAWER-05**: Drawer bottom section "Account" shows current user (avatar + email, read-only) and a Sign out action that calls `signOut()` then `router.push('/auth/sign-in')`
-- [x] **DRAWER-06**: `app/mobile/nav/page.tsx` is deleted in the same change that ships the drawer
+- [ ] **PAX8-13**: A new `/pax8` page lists PAX8 companies with their subscriptions and a cost breakdown
+- [ ] **PAX8-14**: The `/pax8` page surfaces flagged/ambiguous company matches (PAX8-11) for manual resolution
-### DASH — Mobile Dashboard (spec §6.1)
+## Future Requirements
-- [ ] **DASH-01**: 2×2 KPI grid with four primary metric cards drawn from desktop dashboard hero stats
-- [ ] **DASH-02**: "Needs Attention" horizontal-scroll strip of compact cards (overdue tickets, failed backups, stalled workflows); tapping a card opens its detail view
-- [ ] **DASH-03**: Compact backup/worker status row showing analyzer worker, RMM worker, and backup-success-rate; read-only; tap opens desktop admin page
-- [ ] **DASH-04**: No charts/recharts on the mobile Dashboard
+
-### TICK — Mobile Tickets list (spec §6.2)
-
-- [ ] **TICK-01**: Collapsible filter strip at top (`Collapsible` from shadcn), default collapsed; expanded shows status, priority, queue, assigned-to-me toggle
-- [ ] **TICK-02**: Filter state syncs to URL query string for deep-linking
-- [ ] **TICK-03**: List rows have left-edge color stripe by priority (Critical/High/Medium/Low → red/orange/amber/slate); body shows ticket #, title, company, age, assignee
-- [ ] **TICK-04**: Single-tap on a row opens detail page
-- [ ] **TICK-05**: Cursor-based infinite scroll (~25 per page) replaces pagination; next page triggers when last row enters viewport via IntersectionObserver
-- [ ] **TICK-06**: "Load more" fallback button present for accessibility
-- [ ] **TICK-07**: Detail page (`/mobile/tickets/[id]`) header reskinned to match new shell (Wulf mark, breadcrumb back); body kept largely as-is
-
-### FIN — Mobile Finance (spec §6.3)
-
-- [x] **FIN-01**: Page restyled with new Card and typography scale; spacing fixed for small phones
-- [x] **FIN-02**: Wide tables on mobile widths replaced with stacked lists; no new data, no new sections
-
-### ANL — Mobile Analyzer feed (spec §6.4) — NEW PAGE
-
-- [ ] **ANL-01**: `/mobile/analyzer` route exists (read-only feed, most-recent-first stream of AI ticket analyses)
-- [ ] **ANL-02**: Each list row shows ticket #, title, analyzer one-line summary, confidence badge, stage indicator (Triage → Analyze → Deep Review)
-- [ ] **ANL-03**: Tapping a row opens a mobile summary view rendering Summary, Next Step, Next Step Rationale (all already produced by the analyzer pipeline)
-- [ ] **ANL-04**: Summary view includes "View full analysis" link out to the desktop analyzer page
-- [ ] **ANL-05**: No editing, no re-run, no prompt tuning on mobile
-- [ ] **ANL-06**: Source data via `/api/mobile/analyzer/feed` (or reuse an existing list endpoint if it returns the right shape) reading from `analyzer_analyses`
-
-### ENG — Mobile Engagement (spec §6.5) — NEW PAGES
-
-- [ ] **ENG-01**: `/mobile/engagement` overview page (real refactor, not a thin adaptation of the ~1300-line desktop page)
-- [ ] **ENG-02**: Period selector chip row (today / 7d / 30d) sticky just below the page H1
-- [ ] **ENG-03**: Summary cards stacked single-column (active users, total Graph hours, total Autotask hours, hours-per-active-user) — no 4-up grid on phone widths
-- [ ] **ENG-04**: Per-employee list as stacked rows (avatar/initials, name, role, hours bar) with sort control above (sort by hours, name, utilization) and search input
-- [ ] **ENG-05**: Top of list shows compact "hours trend" sparkline scoped to the selected period; no multi-series chart on mobile
-- [ ] **ENG-06**: User profile is `/mobile/engagement/[userId]` (segment form preferred for shareable URLs); single-column layout: identity header → period selector → key metrics (compact) → activity breakdown list → recent items
-- [ ] **ENG-07**: User profile is a real page, not a modal — replaces desktop user-detail modal pattern on mobile so back gesture works
-- [ ] **ENG-08**: Profile reuses existing engagement profile data endpoints; no new data
-- [ ] **ENG-09**: Engagement is reachable from the More drawer, NOT the bottom bar
-
-### TZ — User Timezone (Phase 7.1, urgent insertion)
-
-- [ ] **TZ-01**: Better Auth users table extended with a `timezone` field (IANA string). Default for users with no value = `process.env.DEFAULT_TIMEZONE || 'UTC'`. Existing rows backfill to the default; UTC remains the storage timezone for all date columns
-- [ ] **TZ-02**: Date math that powers dashboards, ticket filters, finance views, and engagement period selectors computes day/week boundaries against the viewing user's `timezone` — NOT UTC, NOT the browser's local zone (browsers may differ from the user's chosen tz, e.g. travel) (Exception: `engagement_snapshots`-derived metrics — active users D7/D30/D90, MS Graph hours — bucket by UTC at sync time and remain UTC-bucketed in this phase; ≤24h drift is accepted because engagement is an admin-overview surface, not an operational date display. Per-user snapshot bucketing is deferred to a future phase if needed.)
-- [ ] **TZ-03**: `GET /api/me/timezone` (auth required) returns `{ timezone, source: 'user' | 'default' }`. `PUT /api/me/timezone` accepts `{ timezone }`, validates against `Intl.supportedValuesOf('timeZone')`, persists to the user row, returns the new value
-- [ ] **TZ-04**: Shared client hook `useUserTimezone()` reads tz from `useSession()` (Better Auth additionalField). All date-formatting and range-bucketing in mobile + desktop pages goes through this hook — no scattered `Intl.DateTimeFormat` instantiations with hardcoded zones
-
-### PROF — Profile page surface (Phase 9)
-
-- [ ] **PROF-01**: `/mobile/profile` exists as a real page (not a modal), gated by `requireAuth()`, accessible from the More drawer
-- [ ] **PROF-02**: Page renders four sections in this order, each a shadcn `Card`: Timezone, Theme, Notifications, Channels
-- [ ] **PROF-03**: Per-section save model — switch/select toggles save on change (debounced 400ms for the notifications matrix); text inputs save on blur or via an explicit Save button next to the input. Sonner toast confirms each save; errors render inline and do NOT optimistically update
-- [ ] **PROF-04**: `MoreDrawer.tsx` Account section gains a "Profile & preferences" link (routes to `/mobile/profile`) above the Sign-out destructive action; the existing user-identity row becomes the tappable link
-
-### TZ-CHOOSER — Timezone chooser UI (Phase 9)
-
-- [ ] **TZ-CHOOSER-01**: Profile Timezone section uses a shadcn `Combobox` (or `Select` if list overflows) populated from `Intl.supportedValuesOf('timeZone')` plus the `EXTRA_ALLOWED_TIMEZONES` constants (`UTC`, `Etc/UTC`, `GMT`, `Etc/GMT`); writes via the existing `PUT /api/me/timezone` endpoint unchanged
-- [ ] **TZ-CHOOSER-02**: Currently-rendering time displayed alongside as read-only (e.g., "Your current time: 2026-05-09 14:32 in America/Chicago"), formatted via `useUserTimezone()`
-
-### THEME — Theme persistence (Phase 9)
-
-- [ ] **THEME-01**: `theme TEXT NOT NULL DEFAULT 'system'` column added to `user` table (allowed values: `'light'` | `'dark'` | `'system'`); existing rows backfilled to `'system'`; exposed via Better Auth `additionalFields` so `session.user.theme` is available the same way `session.user.timezone` is today
-- [ ] **THEME-02**: `GET /api/me/theme` (auth required) returns `{ theme, source: 'user' | 'default' }`. `PUT /api/me/theme` accepts `{ theme }`, validates against the three-string allowlist, writes session.user.id only, returns the new value
-- [ ] **THEME-03**: On session load and after sign-in, a client-side effect compares `session.user.theme` against `useTheme()` and calls `setTheme(session.user.theme)` if different — server is canonical; brief flash on auth boundary is acceptable
-- [ ] **THEME-04**: Existing desktop `ThemeToggle` (`components/theme-toggle.tsx`) becomes session-aware: the `setTheme()` callback also issues `PUT /api/me/theme` so the desktop affordance writes through to the server
-- [ ] **THEME-05**: Default value for users who never visit the settings page is `'system'`; next-themes handles OS-preference detection at render time — zero behavior change for existing signed-in users
-
-### CHAN — Personal notification channels (Phase 9)
-
-- [ ] **CHAN-01**: `notification_channels.owner_user_id TEXT REFERENCES "user"(id) ON DELETE CASCADE` column added; `NULL` = global channel (existing rows), `NOT NULL` = personal channel
-- [ ] **CHAN-02**: A user has at most one personal Teams channel and one personal ntfy channel at a time. The constraint is enforced in the API layer (UPSERT keyed by `(owner_user_id, channel_type)`) rather than via a partial-unique index
-- [ ] **CHAN-03**: ntfy topic is **Pulse-minted** on first save — API generates a UUID-prefixed topic (e.g., `pulse-7f3a9c2b`) and returns it. UI surfaces the subscribe link (`https://ntfy.sh/`) and a QR code so the user can subscribe in their ntfy app. Power users can override via an "Edit advanced" disclosure with a custom topic string
-- [ ] **CHAN-04**: Teams webhook URL is user-supplied free text, validated as an `https://` URL whose host matches `*.webhook.office.com` or `*.logic.azure.com`
-- [ ] **CHAN-05**: On save (Teams URL or first ntfy mint) the API issues a single best-effort test send ("Pulse channel verified — you can ignore this message."). The save itself succeeds even if the test fails; the test result (success / HTTP status / error message) surfaces inline next to the input
-- [ ] **CHAN-06**: Admins (role `admin` or `super-admin`) can read AND edit any user's personal channels via `/admin/workflow/channels` (extended with an Owner column + filter). Non-admin users only see/edit their own personal channels
-- [ ] **CHAN-07**: `/api/me/channels` exposes the user's personal channels — `GET` returns the configured set, `PUT /api/me/channels/teams` and `PUT /api/me/channels/ntfy` upsert the row, `DELETE /api/me/channels/{type}` removes one, `POST /api/me/channels/{type}/test` issues a test send. All routes auth-gated to `session.user.id`
-
-### SUB — Per-event notification subscriptions (Phase 9)
-
-- [ ] **SUB-01**: New table `notify_event_keys` (`key TEXT PRIMARY KEY`, `display_label TEXT NOT NULL`, `description TEXT`, `sort_order INTEGER DEFAULT 0`, `is_active BOOLEAN DEFAULT true`, `created_at TIMESTAMP DEFAULT NOW()`); admins manage it at a new `/admin/workflow/event-keys` page (CRUD on label/description/sort/active). Keys not in the lookup are still routable — the lookup is a humanization layer, not a gate
-- [ ] **SUB-02**: New table `user_event_subscriptions` (`user_id TEXT REFERENCES "user"(id) ON DELETE CASCADE`, `event_key TEXT NOT NULL`, `channel_type VARCHAR(20) NOT NULL`, `enabled BOOLEAN NOT NULL DEFAULT true`, `updated_at TIMESTAMP NOT NULL DEFAULT NOW()`, `PRIMARY KEY (user_id, event_key, channel_type)`). Row absence = default enabled (opt-out model)
-- [ ] **SUB-03**: Profile Notifications section renders a matrix — rows = active `notify_event_keys`, columns = personal channel types the user has configured. When only one personal channel is configured, the matrix collapses to a single Channel column. New event keys go live for everyone immediately (default enabled)
-- [ ] **SUB-04**: `/api/me/notification-subscriptions` — `GET` returns the full matrix for the calling user (joining active event keys with stored rows, defaulting missing rows to enabled), `PUT` writes a single row. Auth-gated to `session.user.id`
-
-### ROUTE — notify.ts per-user routing (Phase 9)
-
-- [ ] **ROUTE-01**: The `notify` step config gains an optional `route_to_user` block: `{ source, field, resolve, event_key, channel_type? }`. When absent, behavior is unchanged (backward compatible). When present, it is attempted before falling back to the step's existing `channel_id`
-- [ ] **ROUTE-02**: Resolvers shipped in v1 — `autotask_resource_email` (joins `resources.email` from a resource ID), `direct_email` (the field IS already an email), `pulse_user_id` (the field IS already a Pulse `user.id`). Resolvers live in `lib/services/pipeline-steps/notify-resolvers.ts` and are registered in a `Map` so adding a resolver is a one-file change
-- [ ] **ROUTE-03**: notify.ts user-route order when `route_to_user` is present: read `context[source][field]` → call resolver → look up Pulse user → check `user_event_subscriptions(event_key, channel_type)` → look up personal channel → send. Each branch can short-circuit per the muting / fallback rules below
-- [ ] **ROUTE-04**: When the user route can't deliver (no personal channel of the requested type, or send returned non-2xx), notify.ts falls back to the step's `channel_id` (global) and records `output.user_route_fallback = { reason: 'no_channel' | 'send_failed' | 'user_not_found', user_id?, channel_type, error? }` on the execution-step row. Step still returns `success: true` (the fallback succeeded)
-- [ ] **ROUTE-05**: When the user has the relevant `(event_key, channel_type)` toggle DISABLED, notify.ts records `output.skipped_reason = 'user_muted'` and does NOT fall back to global — muting must actually mute. Step returns `success: true` (intended skip)
-- [ ] **ROUTE-06**: When `channel_type` is omitted in `route_to_user`, notify.ts attempts `ntfy` first, then `teams`, then global fallback — favoring push semantics for managers on mobile
-- [ ] **ROUTE-07**: `/admin/workflow/executions` page gains a filter "Show executions that fell back to global" — a one-line UI addition that surfaces `user_route_fallback` events so admins can repair broken personal channels
-
-## v2 Requirements
-
-Acknowledged but deferred. Not in this milestone's roadmap.
-
-### NOTIF — Notifications
-
-- **NOTIF-01**: Real notification list behind the Bell icon (replaces SHELL-03 placeholder)
-- **NOTIF-02**: Notification badge logic on the Bell icon
-
-### TABLET — Tablet breakpoint
-
-- **TABLET-01**: `md:max-w-2xl mx-auto` wrapper for tablet widths
-
-### OFFLINE — Offline support
-
-- **OFFLINE-01**: Service worker for offline cache
-- **OFFLINE-02**: Push notifications (requires SW)
-
-### EDIT — Mobile editing
-
-- **EDIT-01**: Mobile editing on Engagement user detail
-- **EDIT-02**: Mobile re-run / prompt edits on Analyzer
+- Write access to PAX8 (seat adjustments, order placement) — no concrete need identified yet; revisit only if reconciliation surfaces one
+- Domain-based or external-ID-based company matching, if research resolves [[RESEARCH-pax8-company-identifiers]] in `.planning/research/questions.md` and finds a more stable join key than name
+- General Pulse data assistant (chatbot) consuming this data — separate future milestone, see `.planning/seeds/SEED-003-general-pulse-data-assistant.md`
## Out of Scope
-Explicitly excluded for v1. Documented to prevent scope creep.
+
-| Feature | Reason |
-|---------|--------|
-| Service worker / offline cache / push notifications | No clear offline use-case yet — defer until one lands (spec §4, §7) |
-| Tablet breakpoint (`md:max-w-2xl`) | Noted as follow-up; keep `max-w-lg` for v1 (spec §4, §7) |
-| Real notification list behind the Bell | Placeholder only this iteration; future phase wires it (spec §5.1, §7) |
-| Mobile editing on Engagement user detail | Read-only on mobile by design (spec §6.5, §7) |
-| Mobile re-run / prompt tuning on Analyzer | Read-only on mobile by design (spec §6.4, §7) |
-| Charts / recharts on mobile Dashboard | Not earning their weight on small widths (spec §6.1, §7) |
-| Restyling/replacing desktop pages reachable from More drawer | Desktop pages stay as-is (spec §7) |
-| Multi-series chart on mobile Engagement overview | Replaced by single sparkline (spec §6.5) |
-| Modal-based user detail on mobile | Replaced by real page so back gesture works (spec §6.5) |
-| `/mobile-v2` parallel directory | Rebuild `/mobile` in place — keep canonical URLs (spec §2) |
-| Per-user-tz `engagement_snapshots` bucketing (TZ-02 carve-out) | Engagement metrics derived from `engagement_snapshots` (active users D7/D30/D90, MS Graph hours) are bucketed by UTC at sync time. Per-user re-bucketing would require either per-request re-bucket (expensive) or per-user snapshot rebuild (doubles storage). ≤24h drift accepted on this admin-overview surface. May revisit in a future phase. |
+- Write-back to PAX8 — this integration is read-only in v2.0, matching the pattern of most existing Pulse integrations (RMM, IT Glue, Veeam)
+- Chatbot/NL query layer — deliberately separated into SEED-003 so this milestone stays scoped to the data sync itself
## Traceability
-Updated during roadmap creation.
-
-| Requirement | Phase | Status |
-|-------------|-------|--------|
-| PWA-01 | Phase 1 | Pending |
-| PWA-02 | Phase 1 | Pending |
-| PWA-03 | Phase 1 | Pending |
-| PWA-04 | Phase 1 | Pending |
-| SHELL-01 | Phase 2 | Complete |
-| SHELL-02 | Phase 2 | Pending |
-| SHELL-03 | Phase 2 | Pending |
-| SHELL-04 | Phase 2 | Pending |
-| SHELL-05 | Phase 2 | Complete |
-| SHELL-06 | Phase 2 | Pending |
-| NAV-01 | Phase 2 | Pending |
-| NAV-02 | Phase 2 | Pending |
-| NAV-03 | Phase 2 | Pending |
-| DRAWER-01 | Phase 2 | Pending |
-| DRAWER-02 | Phase 2 | Pending |
-| DRAWER-03 | Phase 2 | Pending |
-| DRAWER-04 | Phase 2 | Pending |
-| DRAWER-05 | Phase 2 | Pending |
-| DRAWER-06 | Phase 2 | Complete |
-| DASH-01 | Phase 3 | Pending |
-| DASH-02 | Phase 3 | Pending |
-| DASH-03 | Phase 3 | Pending |
-| DASH-04 | Phase 3 | Pending |
-| TICK-01 | Phase 4 | Pending |
-| TICK-02 | Phase 4 | Pending |
-| TICK-03 | Phase 4 | Pending |
-| TICK-04 | Phase 4 | Pending |
-| TICK-05 | Phase 4 | Pending |
-| TICK-06 | Phase 4 | Pending |
-| TICK-07 | Phase 4 | Pending |
-| FIN-01 | Phase 5 | Complete |
-| FIN-02 | Phase 5 | Complete |
-| ANL-01 | Phase 6 | Pending |
-| ANL-02 | Phase 6 | Pending |
-| ANL-03 | Phase 6 | Pending |
-| ANL-04 | Phase 6 | Pending |
-| ANL-05 | Phase 6 | Pending |
-| ANL-06 | Phase 6 | Pending |
-| ENG-01 | Phase 7 | Pending |
-| ENG-02 | Phase 7 | Pending |
-| ENG-03 | Phase 7 | Pending |
-| ENG-04 | Phase 7 | Pending |
-| ENG-05 | Phase 7 | Pending |
-| ENG-09 | Phase 7 | Pending |
-| ENG-06 | Phase 8 | Pending |
-| ENG-07 | Phase 8 | Pending |
-| ENG-08 | Phase 8 | Pending |
-
-**Coverage:**
-- v1 requirements: 47 total
-- Mapped to phases: 47
-- Unmapped: 0 ✓
-
-**Per-phase counts:**
-- Phase 1 (PWA Scaffolding): 4 requirements
-- Phase 2 (Mobile Shell + More Drawer): 15 requirements
-- Phase 3 (Dashboard Restyle): 4 requirements
-- Phase 4 (Tickets Restyle): 7 requirements
-- Phase 5 (Finance Restyle): 2 requirements
-- Phase 6 (Analyzer Feed): 6 requirements
-- Phase 7 (Engagement Overview): 6 requirements
-- Phase 8 (Engagement User Profile): 3 requirements
-- Phase 9 (User Profile & Preferences): 31 requirements (PROF-01..04, TZ-CHOOSER-01..02, THEME-01..05, CHAN-01..07, SUB-01..04, ROUTE-01..07)
+
---
-*Requirements defined: 2026-05-03*
-*Last updated: 2026-05-09 — Phase 9 requirements added (PROF, TZ-CHOOSER, THEME, CHAN, SUB, ROUTE) after `/gsd-discuss-phase 9`*
-
-
\ No newline at end of file
+*Requirements defined: 2026-07-10*