docs(phase-01): complete phase execution
This commit is contained in:
parent
ca91390f1a
commit
8e6dcf01e1
3 changed files with 330 additions and 7 deletions
153
.planning/ROADMAP.md
Normal file
153
.planning/ROADMAP.md
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
# Roadmap: Pulse Mobile Shell Redesign
|
||||
|
||||
## Overview
|
||||
|
||||
Eight phases mirror the deliberate build order in the source spec
|
||||
(`docs/superpowers/specs/2026-05-03-mobile-shell-design.md` §8). Each phase
|
||||
ships independently to `master` — no big-bang merge. Phase 1 lays PWA
|
||||
metadata and safe-area utilities. Phase 2 rebuilds `app/mobile/layout.tsx`
|
||||
with the new header, 5-cell bottom nav, and More drawer (deleting
|
||||
`/mobile/nav` in the same change). Once the shell lands, Phases 3–7 are
|
||||
independent restyles/new pages and may be executed in parallel; Phase 8
|
||||
follows Phase 7 because the user profile is reached from the Engagement
|
||||
overview. All work happens in place under `/mobile/*` — no `/mobile-v2`,
|
||||
no parallel routes.
|
||||
|
||||
## Phases
|
||||
|
||||
**Phase Numbering:**
|
||||
- Integer phases (1, 2, 3): Planned milestone work
|
||||
- Decimal phases (2.1, 2.2): Urgent insertions (marked with INSERTED)
|
||||
|
||||
Decimal phases appear between their surrounding integers in numeric order.
|
||||
|
||||
- [ ] **Phase 1: PWA Scaffolding** — Manifest, viewport meta, and safe-area utilities so the shell installs and paints under the home indicator
|
||||
- [ ] **Phase 2: Mobile Shell + More Drawer** — New `app/mobile/layout.tsx` (header + 5-cell bottom nav) and Sheet drawer that replaces `/mobile/nav`
|
||||
- [ ] **Phase 3: Dashboard Restyle** — 2×2 KPI grid, Needs Attention strip, worker/backup status row (no charts)
|
||||
- [ ] **Phase 4: Tickets Restyle** — Collapsible URL-synced filters, priority-bar rows, cursor-based infinite scroll, detail header reskin
|
||||
- [ ] **Phase 5: Finance Restyle** — Adopt new Card + typography scale, swap wide tables for stacked lists
|
||||
- [ ] **Phase 6: Analyzer Feed (NEW)** — `/mobile/analyzer` read-only stream + `/api/mobile/analyzer/feed`
|
||||
- [ ] **Phase 7: Engagement Overview (NEW)** — `/mobile/engagement` phone-first overview reachable from the More drawer
|
||||
- [ ] **Phase 8: Engagement User Profile (NEW)** — `/mobile/engagement/[userId]` real-page profile that replaces the desktop modal pattern
|
||||
|
||||
## Phase Details
|
||||
|
||||
### Phase 1: PWA Scaffolding
|
||||
**Goal**: A manager who taps "Add to Home Screen" gets a standalone Pulse icon that opens to the mobile shell with content respecting the device safe areas.
|
||||
**Depends on**: Nothing (first phase)
|
||||
**Requirements**: PWA-01, PWA-02, PWA-03, PWA-04
|
||||
**Success Criteria** (what must be TRUE):
|
||||
1. Visiting `/manifest.json` returns valid JSON with `name: "Pulse"`, `display: "standalone"`, `start_url: "/mobile"`, and theme/background colors matching the app shells
|
||||
2. The root `app/layout.tsx` references the manifest via `<link rel="manifest">` and the viewport meta includes `viewport-fit=cover`
|
||||
3. A safe-area utility (Tailwind arbitrary values or shared class) is available so any sticky top/bottom bar can opt into `env(safe-area-inset-top)` / `env(safe-area-inset-bottom)` padding
|
||||
4. Installing Pulse to a phone home screen launches a chromeless app pointed at `/mobile` (no service worker, no offline)
|
||||
**Plans**: 2 plans
|
||||
- [x] 01-01-PLAN.md — Web App Manifest + viewport-fit=cover (PWA-01, PWA-02, PWA-03)
|
||||
- [x] 01-02-PLAN.md — Safe-area `pt-safe` / `pb-safe` @utility blocks in brand.css (PWA-04, gap closure)
|
||||
**UI hint**: no
|
||||
|
||||
### Phase 2: Mobile Shell + More Drawer
|
||||
**Goal**: Every `/mobile/*` page renders inside a new layout — sticky header (Wulf mark + Bell placeholder + avatar), scrollable content, and a 5-cell bottom nav whose fifth control opens a Sheet drawer that fully replaces `/mobile/nav`.
|
||||
**Depends on**: Phase 1
|
||||
**Requirements**: SHELL-01, SHELL-02, SHELL-03, SHELL-04, SHELL-05, SHELL-06, NAV-01, NAV-02, NAV-03, DRAWER-01, DRAWER-02, DRAWER-03, DRAWER-04, DRAWER-05, DRAWER-06
|
||||
**Success Criteria** (what must be TRUE):
|
||||
1. On any `/mobile/*` route the user sees a sticky header with the Wulf wordmark linking to `/mobile/dashboard`, a Bell icon button (keyboard-focusable, no menu), and a compact avatar — no page title in the header
|
||||
2. A fixed bottom bar exposes four primary tabs (Dashboard, Tickets, Finance, Analyzer) plus a More cell; tapping a tab routes to its page and the active tab uses `text-primary` based on `pathname.startsWith(href)`
|
||||
3. Tapping More (or the header avatar) opens a single Sheet drawer with three sections — Mobile sections (Engagement), Full site (Quotes, Configuration Items, Backup Status, Ticket Digest, Admin/Sync — each with an `ExternalLink` hint), and Account (current user read-only + Sign out)
|
||||
4. Tapping Sign out in the drawer signs the user out and lands them on `/auth/sign-in`
|
||||
5. `app/mobile/nav/page.tsx` no longer exists; visiting `/mobile/nav` does not render the old standalone nav page
|
||||
6. Page content scrolls under the sticky header and is not hidden behind the bottom nav (bottom padding accounts for nav height + safe-area inset)
|
||||
**Plans**: TBD
|
||||
**UI hint**: yes
|
||||
|
||||
### Phase 3: Dashboard Restyle
|
||||
**Goal**: A manager opening `/mobile/dashboard` sees the state of the business at a glance — four KPIs, items needing attention, and a worker/backup status row — with no charts.
|
||||
**Depends on**: Phase 2
|
||||
**Requirements**: DASH-01, DASH-02, DASH-03, DASH-04
|
||||
**Success Criteria** (what must be TRUE):
|
||||
1. Dashboard renders a 2×2 grid of four primary KPI cards drawn from desktop hero stats (no 1×4 row, no charts)
|
||||
2. Below the grid, a "Needs Attention" horizontally-scrollable strip surfaces overdue tickets, failed backups, and stalled workflows; tapping a card opens its detail view
|
||||
3. A compact status row shows analyzer worker, RMM worker, and backup-success-rate; tapping any element opens the corresponding desktop admin page
|
||||
4. The page contains no recharts/chart components on phone widths
|
||||
**Plans**: TBD
|
||||
**UI hint**: yes
|
||||
|
||||
### Phase 4: Tickets Restyle
|
||||
**Goal**: A manager triages tickets on a phone with a collapsible filter bar that deep-links via URL, priority-coloured rows, and infinite scroll — and the detail page header matches the new shell.
|
||||
**Depends on**: Phase 2
|
||||
**Requirements**: TICK-01, TICK-02, TICK-03, TICK-04, TICK-05, TICK-06, TICK-07
|
||||
**Success Criteria** (what must be TRUE):
|
||||
1. The Tickets page opens with the filter strip collapsed; expanding it reveals status, priority, queue, and an assigned-to-me toggle, and changing any filter updates the URL query string (deep link works on reload)
|
||||
2. Each list row has a left-edge stripe matching priority (Critical/High/Medium/Low → red/orange/amber/slate) and shows ticket #, title, company, age, and assignee
|
||||
3. Single-tapping a row navigates to `/mobile/tickets/[id]`
|
||||
4. Scrolling to the bottom of the list automatically loads the next ~25 rows (no Next button); a "Load more" fallback button is also visible/focusable for accessibility
|
||||
5. The detail page header uses the new shell styling (Wulf mark, breadcrumb back) while the body remains largely unchanged
|
||||
**Plans**: TBD
|
||||
**UI hint**: yes
|
||||
|
||||
### Phase 5: Finance Restyle
|
||||
**Goal**: A manager reading AR / invoice / payment status on a phone sees properly spaced cards and stacked lists instead of squished wide tables — same data, new shell.
|
||||
**Depends on**: Phase 2
|
||||
**Requirements**: FIN-01, FIN-02
|
||||
**Success Criteria** (what must be TRUE):
|
||||
1. `/mobile/finance` adopts the new Card and typography scale — no horizontal overflow, spacing legible on small phones
|
||||
2. Sections that previously rendered wide tables on phone widths now render as stacked lists (no new sections, no new data sources)
|
||||
**Plans**: TBD
|
||||
**UI hint**: yes
|
||||
|
||||
### Phase 6: Analyzer Feed (NEW)
|
||||
**Goal**: A manager taps the Analyzer tab and skims a most-recent-first stream of AI ticket analyses, opening any one to a phone-friendly summary view that links out to desktop for full details.
|
||||
**Depends on**: Phase 2
|
||||
**Requirements**: ANL-01, ANL-02, ANL-03, ANL-04, ANL-05, ANL-06
|
||||
**Success Criteria** (what must be TRUE):
|
||||
1. Tapping the Analyzer tab in the bottom nav lands on `/mobile/analyzer` and shows a most-recent-first list of AI ticket analyses
|
||||
2. Each row shows ticket #, title, the analyzer's one-line summary, a confidence badge, and a stage indicator (Triage → Analyze → Deep Review)
|
||||
3. Tapping a row opens a mobile summary view rendering Summary, Next Step, and Next Step Rationale, with a "View full analysis" link out to the desktop analyzer page
|
||||
4. The mobile feed never exposes editing, re-run, or prompt-tuning controls (read-only by design)
|
||||
5. The list reads from `analyzer_analyses` via `/api/mobile/analyzer/feed` (or a reused list endpoint that already returns the right shape)
|
||||
**Plans**: TBD
|
||||
**UI hint**: yes
|
||||
|
||||
### Phase 7: Engagement Overview (NEW)
|
||||
**Goal**: A manager reaches Engagement from the More drawer and sees a phone-first overview — period chips, stacked summary cards, a sortable per-employee list, and one compact sparkline.
|
||||
**Depends on**: Phase 2
|
||||
**Requirements**: ENG-01, ENG-02, ENG-03, ENG-04, ENG-05, ENG-09
|
||||
**Success Criteria** (what must be TRUE):
|
||||
1. The Mobile sections row in the More drawer links to `/mobile/engagement`; the Analyzer is on the bottom bar but Engagement is not
|
||||
2. The overview page shows a period selector (today / 7d / 30d) sticky just below the H1, with active period clearly indicated
|
||||
3. Summary cards (active users, total Graph hours, total Autotask hours, hours-per-active-user) render single-column stacked — no 4-up grid on phone widths
|
||||
4. The per-employee list renders as stacked rows (avatar/initials, name, role, hours bar) with a search input and a sort control above (sort by hours, name, utilization)
|
||||
5. A single compact "hours trend" sparkline renders at the top of the list, scoped to the selected period — no multi-series chart
|
||||
**Plans**: TBD
|
||||
**UI hint**: yes
|
||||
|
||||
### Phase 8: Engagement User Profile (NEW)
|
||||
**Goal**: From the Engagement overview, a manager taps an employee row and arrives at a real, shareable profile page — single-column phone-first — and the device back gesture returns them to the overview.
|
||||
**Depends on**: Phase 7
|
||||
**Requirements**: ENG-06, ENG-07, ENG-08
|
||||
**Success Criteria** (what must be TRUE):
|
||||
1. Tapping a row in the per-employee list navigates to `/mobile/engagement/[userId]` (segment form, shareable URL)
|
||||
2. The profile is a real page (not a modal) — the device/browser back gesture returns to the overview at the same scroll position
|
||||
3. The profile renders single-column: identity header → period selector → key metrics (compact) → activity breakdown list → recent items, sourced from the existing engagement profile data endpoints (no new data)
|
||||
**Plans**: TBD
|
||||
**UI hint**: yes
|
||||
|
||||
## Progress
|
||||
|
||||
**Execution Order:**
|
||||
Phases execute in numeric order. Phase 2 unblocks Phases 3–7 (any order, parallelizable). Phase 8 follows Phase 7.
|
||||
|
||||
| Phase | Plans Complete | Status | Completed |
|
||||
|-------|----------------|--------|-----------|
|
||||
| 1. PWA Scaffolding | 1/2 | Executing | - |
|
||||
| 2. Mobile Shell + More Drawer | 0/TBD | Not started | - |
|
||||
| 3. Dashboard Restyle | 0/TBD | Not started | - |
|
||||
| 4. Tickets Restyle | 0/TBD | Not started | - |
|
||||
| 5. Finance Restyle | 0/TBD | Not started | - |
|
||||
| 6. Analyzer Feed | 0/TBD | Not started | - |
|
||||
| 7. Engagement Overview | 0/TBD | Not started | - |
|
||||
| 8. Engagement User Profile | 0/TBD | Not started | - |
|
||||
|
||||
---
|
||||
*Roadmap created: 2026-05-03*
|
||||
*Source spec: `docs/superpowers/specs/2026-05-03-mobile-shell-design.md`*
|
||||
|
|
@ -1,3 +1,19 @@
|
|||
---
|
||||
gsd_state_version: 1.0
|
||||
milestone: v1.0
|
||||
milestone_name: milestone
|
||||
status: executing
|
||||
stopped_at: Roadmap created — ready to plan Phase 1
|
||||
last_updated: "2026-05-03T18:05:22.540Z"
|
||||
last_activity: 2026-05-03
|
||||
progress:
|
||||
total_phases: 8
|
||||
completed_phases: 1
|
||||
total_plans: 2
|
||||
completed_plans: 2
|
||||
percent: 100
|
||||
---
|
||||
|
||||
# Project State
|
||||
|
||||
## Project Reference
|
||||
|
|
@ -5,21 +21,22 @@
|
|||
See: .planning/PROJECT.md (updated 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.
|
||||
**Current focus:** Phase 1 — PWA Scaffolding
|
||||
**Current focus:** Phase 01 — pwa-scaffolding
|
||||
|
||||
## Current Position
|
||||
|
||||
Phase: 1 of 8 (PWA Scaffolding)
|
||||
Plan: 0 of TBD in current phase
|
||||
Status: Ready to plan
|
||||
Last activity: 2026-05-03 — Roadmap created (8 phases, 47/47 v1 requirements mapped)
|
||||
Phase: 2
|
||||
Plan: Not started
|
||||
Status: Ready to execute
|
||||
Last activity: 2026-05-03
|
||||
|
||||
Progress: [░░░░░░░░░░] 0%
|
||||
|
||||
## Performance Metrics
|
||||
|
||||
**Velocity:**
|
||||
- Total plans completed: 0
|
||||
|
||||
- Total plans completed: 2
|
||||
- Average duration: —
|
||||
- Total execution time: 0.0 hours
|
||||
|
||||
|
|
@ -27,9 +44,10 @@ Progress: [░░░░░░░░░░] 0%
|
|||
|
||||
| Phase | Plans | Total | Avg/Plan |
|
||||
|-------|-------|-------|----------|
|
||||
| - | - | - | - |
|
||||
| 01 | 2 | - | - |
|
||||
|
||||
**Recent Trend:**
|
||||
|
||||
- Last 5 plans: —
|
||||
- Trend: —
|
||||
|
||||
|
|
|
|||
152
.planning/phases/01-pwa-scaffolding/01-VERIFICATION.md
Normal file
152
.planning/phases/01-pwa-scaffolding/01-VERIFICATION.md
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
---
|
||||
phase: 01-pwa-scaffolding
|
||||
verified: 2026-05-03T18:05:00Z
|
||||
status: human_needed
|
||||
score: 4/4 must-haves verified
|
||||
re_verification:
|
||||
previous_status: gaps_found
|
||||
previous_score: 3/4
|
||||
gaps_closed:
|
||||
- "A safe-area utility (Tailwind arbitrary values or shared class) is available so any sticky top/bottom bar can opt into env(safe-area-inset-top) / env(safe-area-inset-bottom) padding"
|
||||
gaps_remaining: []
|
||||
regressions: []
|
||||
human_verification:
|
||||
- test: "Install Pulse on a real iPhone and confirm Add-to-Home-Screen launches a chromeless app at /mobile"
|
||||
expected: "Tapping the installed icon opens Pulse with no Safari chrome (no URL bar, no tabs); landing route is /mobile; status bar tints match theme_color #0075AD on iOS, background flashes #FFFFFF then renders the mobile shell"
|
||||
why_human: "Real PWA install behavior (chromeless launch, system UI tint, splash background) cannot be verified programmatically — Chrome DevTools 'Add to Home Screen' simulation is approximate but only a real device exercises Safari's manifest pickup, status bar colorization, and splash rendering. Required by ROADMAP Phase 1 SC #4."
|
||||
- test: "Install Pulse on a real Android device (Chrome) and confirm the install banner appears, the icon shows on the home screen, and tapping it lands on /mobile in standalone mode"
|
||||
expected: "Chrome shows an install prompt (or 'Add to Home Screen' from menu); the installed icon uses one of the configured PNGs; launching opens a standalone window with no browser chrome; URL bar is hidden; navigating beyond /mobile (scope='/') stays in-app"
|
||||
why_human: "Same as above — requires a real device (Chromium PWA install heuristics depend on visit count, manifest validation, and platform). Required by ROADMAP Phase 1 SC #4 and PWA-01 wording."
|
||||
- test: "Confirm `viewport-fit=cover`, `<link rel=\"manifest\">`, and the new `.pt-safe` / `.pb-safe` rules render in a real browser when the dev server is running"
|
||||
expected: "View source on http://localhost:3100/ and confirm `<meta name=\"viewport\" content=\"...viewport-fit=cover...\">` is present, plus `<link rel=\"manifest\" href=\"/manifest.json\">`. Inspect the live CSS bundle and confirm `.pt-safe { padding-top: env(safe-area-inset-top) }` and `.pb-safe { padding-bottom: env(safe-area-inset-bottom) }` are emitted (already confirmed in the .next build artifact during this re-verification — repeat against the live dev server)."
|
||||
why_human: "Dev server was not running during this verification pass; the static checks (file contents, type-check, production build artifact inspection) prove the metadata API and Tailwind 4 @utility blocks compile correctly, but a smoke test against the running app proves the runtime serializes as expected. Optional/routine — can be done by anyone with `npm run dev` access."
|
||||
---
|
||||
|
||||
# Phase 01: PWA Scaffolding Verification Report (Re-verification)
|
||||
|
||||
**Phase Goal:** A manager who taps "Add to Home Screen" gets a standalone Pulse icon that opens to the mobile shell with content respecting the device safe areas.
|
||||
**Verified:** 2026-05-03T18:05:00Z (re-verification after PWA-04 gap closure)
|
||||
**Status:** human_needed (all programmatic checks pass; awaiting real-device install verification)
|
||||
**Re-verification:** Yes — after gap closure (plan 01-02 closed PWA-04 by adding `pt-safe` / `pb-safe` `@utility` blocks to `app/styles/brand.css`)
|
||||
|
||||
## Re-verification Summary
|
||||
|
||||
| Item | Previous (initial verification) | Current (after 01-02) |
|
||||
|------|-------------------------------|----------------------|
|
||||
| Status | `gaps_found` | `human_needed` |
|
||||
| Score | 3/4 | **4/4** |
|
||||
| Truth #3 (safe-area utility) | ✗ FAILED — no utility, no arbitrary-value usage anywhere in repo | ✓ VERIFIED — `@utility pt-safe` and `@utility pb-safe` defined in `app/styles/brand.css`, compiled into production CSS bundle |
|
||||
| PWA-04 traceability | ORPHANED — no plan claimed it | SATISFIED — `01-02-PLAN.md` declares `requirements: [PWA-04]`; `01-02-SUMMARY.md` lists it under "Requirements Satisfied" |
|
||||
| Regressions introduced by 01-02 | n/a | None — Truths 1, 2, 4 unchanged; manifest, layout, icons all intact |
|
||||
|
||||
The only outstanding item is the human verification block (real-device install tests + optional live dev-server smoke test). These are not gaps — they are the same items that were routed to humans in the initial verification, plus a small addition asking the human to confirm the new `.pt-safe` / `.pb-safe` rules render at runtime (already confirmed in the production CSS bundle artifact, but a live dev-server check costs nothing).
|
||||
|
||||
## Goal Achievement
|
||||
|
||||
The phase now delivers the goal in full at the artifact level:
|
||||
- **Manifest** — correct fields, correct icons, correct start_url
|
||||
- **Viewport** — `viewport-fit=cover` exported via Next.js 16 Viewport API
|
||||
- **Safe-area utility** — `pt-safe` / `pb-safe` `@utility` blocks in `brand.css`, compiled to `.pt-safe { padding-top: env(safe-area-inset-top) }` / `.pb-safe { padding-bottom: env(safe-area-inset-bottom) }` in the production CSS bundle, ready for Phase 2's sticky header (SHELL-05) and fixed bottom nav (SHELL-06) to consume
|
||||
|
||||
The remaining "human_needed" status is purely about real-device install behavior (which cannot be programmatically verified) — not about missing code.
|
||||
|
||||
### Observable Truths
|
||||
|
||||
| # | Truth | Status | Evidence |
|
||||
| --- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 1 | Visiting `/manifest.json` returns valid JSON with `name: "Pulse"`, `display: "standalone"`, `start_url: "/mobile"`, and theme/background colors matching the app shells | ✓ VERIFIED | `public/manifest.json` exists; jq validation passes (`name=Pulse`, `short_name=Pulse`, `display=standalone`, `start_url=/mobile`, `theme_color=#0075AD`, `background_color=#FFFFFF`); 3 icons present and the referenced PNG files all exist on disk |
|
||||
| 2 | The root `app/layout.tsx` references the manifest via `<link rel="manifest">` and the viewport meta includes `viewport-fit=cover` | ✓ VERIFIED | `app/layout.tsx:31` `manifest: "/manifest.json"` in metadata (Next 16 emits the link tag); `app/layout.tsx:42-50` exports `viewport: Viewport` with `viewportFit: "cover"`, `width: device-width`, `initialScale: 1`, paired light/dark themeColor; `tsc --noEmit` exits 0 |
|
||||
| 3 | A safe-area utility (Tailwind arbitrary values or shared class) is available so any sticky top/bottom bar can opt into `env(safe-area-inset-top)` / `env(safe-area-inset-bottom)` padding | ✓ VERIFIED (was ✗ FAILED) | `app/styles/brand.css:157-163` defines `@utility pt-safe { padding-top: env(safe-area-inset-top); }` and `@utility pb-safe { padding-bottom: env(safe-area-inset-bottom); }`; brand.css is imported by `app/globals.css:125` (unchanged); production CSS bundle (`.next/static/chunks/3c3ee60b60fe53db.css`) contains the compiled rules `.pt-safe{padding-top:env(safe-area-inset-top)}` and `.pb-safe{padding-bottom:env(safe-area-inset-bottom)}`; `npm run build` exits 0 |
|
||||
| 4 | Installing Pulse to a phone home screen launches a chromeless app pointed at `/mobile` (no service worker, no offline) | ? UNCERTAIN | Manifest fields are correct for this outcome (`display=standalone`, `start_url=/mobile`, no `serviceworker` field, no `next-pwa` dep, no `public/sw.js`). Real install behavior must be verified on a physical device — see Human Verification section. (Unchanged from initial verification.) |
|
||||
|
||||
**Score:** **4/4** truths verified (1 routed to human verification for real-device confirmation)
|
||||
|
||||
### Required Artifacts
|
||||
|
||||
| Artifact | Expected | Status | Details |
|
||||
| ------------------- | ------------------------------------------------------------------------------ | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `public/manifest.json` | Web App Manifest with required fields and ≥1 icon | ✓ VERIFIED | Exists (31 lines), valid JSON, all required fields present with spec-mandated values, 3 icons referencing real assets in `/public` |
|
||||
| `app/layout.tsx` | Root layout exporting metadata.manifest and viewport with viewportFit:"cover" | ✓ VERIFIED | Both exports present; `Metadata` and `Viewport` named imports; RootLayout body unchanged; type check passes |
|
||||
| `app/styles/brand.css` (NEW for re-verification) | Two new `@utility` blocks (`pt-safe`, `pb-safe`) sitting alongside existing utilities | ✓ VERIFIED | Lines 157-163: both `@utility` blocks present with correct `env(safe-area-inset-top/bottom)` declarations; placed between existing `@utility tagline` (line 132) and `/* === Wolf-mark watermark === */` section header (line 165); existing utilities (`num`, `metric-label`, `surface-brand`, `tagline`, `has-mark-watermark`, `.mark-watermark`) and brand tokens (`--wulf-blue`, etc.) all unchanged |
|
||||
|
||||
### Key Link Verification
|
||||
|
||||
| From | To | Via | Status | Details |
|
||||
| ------------------------------------- | ------------------------------- | ---------------------------------------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `app/layout.tsx` | `public/manifest.json` | `metadata.manifest = "/manifest.json"` | ✓ WIRED | Line 31 sets the field; Next.js 16's metadata API emits `<link rel="manifest" href="/manifest.json" />` automatically |
|
||||
| `public/manifest.json` | `/mobile` | `start_url` field | ✓ WIRED | Line 5: `"start_url": "/mobile"` (exact spec match) |
|
||||
| `app/layout.tsx` (viewport export) | rendered `<meta name="viewport">` | Next.js viewport export → viewport-fit=cover | ✓ WIRED | Line 45: `viewportFit: "cover"`; Next 16 documented to serialize this as `viewport-fit=cover` in the rendered meta tag |
|
||||
| `app/styles/brand.css` (`@utility pt-safe`) (NEW) | rendered CSS class `.pt-safe` | Tailwind 4 `@utility` block compilation | ✓ WIRED | Line 157 defines the block; Tailwind 4 build emits `.pt-safe{padding-top:env(safe-area-inset-top)}` into `.next/static/chunks/3c3ee60b60fe53db.css` |
|
||||
| `app/styles/brand.css` (`@utility pb-safe`) (NEW) | rendered CSS class `.pb-safe` | Tailwind 4 `@utility` block compilation | ✓ WIRED | Line 161 defines the block; Tailwind 4 build emits `.pb-safe{padding-bottom:env(safe-area-inset-bottom)}` into the same CSS chunk |
|
||||
| `app/globals.css` (NEW link traced) | `app/styles/brand.css` | `@import "./styles/brand.css"` on line 125 | ✓ WIRED | Pre-existing import — no change required; pulls the new utilities into the global stylesheet automatically |
|
||||
|
||||
### Data-Flow Trace (Level 4)
|
||||
|
||||
N/A — this phase produces only static metadata (manifest JSON + Next.js metadata/viewport exports + Tailwind 4 `@utility` blocks). No dynamic data flow to trace. The CSS-bundle inspection in Truth #3 acts as the equivalent "did the artifact actually flow through compilation" check for static styles.
|
||||
|
||||
### Behavioral Spot-Checks
|
||||
|
||||
| Behavior | Command | Result | Status |
|
||||
| ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | ------------------- | ------ |
|
||||
| Manifest is valid JSON with all spec fields | `jq -e '.name=="Pulse" and .display=="standalone" and .start_url=="/mobile" and .theme_color=="#0075AD" and .background_color=="#FFFFFF" and (.icons\|length)>=1' public/manifest.json` | `true` | ✓ PASS |
|
||||
| Manifest contains no service-worker field | `jq -e '.serviceworker == null' public/manifest.json` | `true` | ✓ PASS |
|
||||
| Layout references manifest | `grep 'manifest: "/manifest.json"' app/layout.tsx` | match | ✓ PASS |
|
||||
| Layout exports `viewportFit: "cover"` | `grep 'viewportFit: "cover"' app/layout.tsx` | match | ✓ PASS |
|
||||
| `Metadata, Viewport` both imported from `next` | `grep 'import type { Metadata, Viewport } from "next"' app/layout.tsx` | match | ✓ PASS |
|
||||
| **`pt-safe` `@utility` block exists** (NEW) | `grep -E '@utility pt-safe' app/styles/brand.css` | match (line 157) | ✓ PASS |
|
||||
| **`pb-safe` `@utility` block exists** (NEW) | `grep -E '@utility pb-safe' app/styles/brand.css` | match (line 161) | ✓ PASS |
|
||||
| **`safe-area-inset-top` declaration correct** (NEW) | `grep -E 'padding-top:\s*env\(safe-area-inset-top\)' app/styles/brand.css` | match (line 158) | ✓ PASS |
|
||||
| **`safe-area-inset-bottom` declaration correct** (NEW) | `grep -E 'padding-bottom:\s*env\(safe-area-inset-bottom\)' app/styles/brand.css` | match (line 162) | ✓ PASS |
|
||||
| **`brand.css` is still imported by `globals.css`** (NEW) | `grep -E '@import "\./styles/brand\.css"' app/globals.css` | match (line 125) | ✓ PASS |
|
||||
| **Out-of-scope `pl-safe` / `pr-safe` NOT added** (NEW) | `grep -E '@utility (pl-safe\|pr-safe)' app/styles/brand.css` | no match (exit 1) | ✓ PASS |
|
||||
| **Production build succeeds with new utilities** (NEW — load-bearing) | `npm run build` | exit 0, "Compiled successfully" | ✓ PASS |
|
||||
| **Compiled `.pt-safe` rule in production CSS bundle** (NEW) | `grep -oE '\.pt-safe[^,{]*\{[^}]*\}' .next/static/chunks/3c3ee60b60fe53db.css` | `.pt-safe{padding-top:env(safe-area-inset-top)}` | ✓ PASS |
|
||||
| **Compiled `.pb-safe` rule in production CSS bundle** (NEW) | `grep -oE '\.pb-safe[^,{]*\{[^}]*\}' .next/static/chunks/3c3ee60b60fe53db.css` | `.pb-safe{padding-bottom:env(safe-area-inset-bottom)}` | ✓ PASS |
|
||||
| TypeScript type check passes | `npx tsc --noEmit --pretty` | exit 0 | ✓ PASS |
|
||||
| No service worker file shipped | `test ! -f public/sw.js && test ! -f public/service-worker.js` | exit 0 | ✓ PASS |
|
||||
| `next-pwa` not added as dependency | `! grep '"next-pwa"' package.json` | not found | ✓ PASS |
|
||||
| **No `tailwind.config.*` created** (NEW) | `test ! -f tailwind.config.{ts,js,mjs}` | exit 0 | ✓ PASS |
|
||||
| All referenced icon PNGs exist on disk | `test -f public/wulff-logo.png && test -f public/favicon.png && test -f public/branding/wulf-mark.png` | exit 0 | ✓ PASS |
|
||||
| Plan commits exist in git history | `git log --oneline \| grep -E '3e3df24\|d196d22\|dff0264'` | all three found | ✓ PASS |
|
||||
| Live manifest fetch (dev server) | `curl -sf http://localhost:3100/manifest.json` | dev server not up | ? SKIP |
|
||||
| Live viewport meta tag rendering | `curl -s http://localhost:3100/ \| grep viewport-fit=cover` | dev server not up | ? SKIP |
|
||||
|
||||
### Requirements Coverage
|
||||
|
||||
| Requirement | Source Plan | Description | Status | Evidence |
|
||||
| ----------- | ----------- | -------------------------------------------------------------------------------------------------------- | ------------- | ----------------------------------------------------------------------------------------------------------------- |
|
||||
| PWA-01 | 01-01 | manifest.json exists with name "Pulse", short_name "Pulse", display "standalone", start_url "/mobile", theme/background colors | ✓ SATISFIED | `public/manifest.json` lines 2-10; jq validation passes |
|
||||
| PWA-02 | 01-01 | Manifest referenced from `app/layout.tsx` via `<link rel="manifest">` | ✓ SATISFIED | `app/layout.tsx:31` `manifest: "/manifest.json"` (Next 16 metadata API emits the link tag) |
|
||||
| PWA-03 | 01-01 | Viewport meta in `app/layout.tsx` includes `viewport-fit=cover` | ✓ SATISFIED | `app/layout.tsx:42-50` exports `viewport: Viewport` with `viewportFit: "cover"` |
|
||||
| PWA-04 | **01-02** (gap closure) | Header and bottom tab bar respect `env(safe-area-inset-top/bottom)` (Tailwind arbitrary values or shared utility class) | ✓ SATISFIED (was ✗ BLOCKED / ORPHANED) | `01-02-PLAN.md` declares `requirements: [PWA-04]` and `01-02-SUMMARY.md` lists it under "Requirements Satisfied". `app/styles/brand.css` defines `@utility pt-safe` (line 157) and `@utility pb-safe` (line 161), compiled into production CSS bundle as `.pt-safe{padding-top:env(safe-area-inset-top)}` and `.pb-safe{padding-bottom:env(safe-area-inset-bottom)}`. Available for Phase 2's sticky header (SHELL-05) and fixed bottom nav (SHELL-06) to consume. ROADMAP Phase 1 SC #3 also satisfied. |
|
||||
|
||||
**Orphaned-requirement state CLOSED.** Every requirement ID assigned to Phase 1 in REQUIREMENTS.md (PWA-01, PWA-02, PWA-03, PWA-04) is now claimed by a plan in this phase and verified against the codebase.
|
||||
|
||||
### Anti-Patterns Found
|
||||
|
||||
| File | Line | Pattern | Severity | Impact |
|
||||
| --------------------- | ---- | ------------------------------------------ | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
No anti-patterns found in the gap-closure scope. The previous warnings on `01-01-PLAN.md` line 281 and `01-01-SUMMARY.md` lines 119-121 (which flagged the silent deferral of PWA-04 to Phase 2) are resolved by the explicit `01-02-PLAN.md` + `01-02-SUMMARY.md` trail that claims and closes PWA-04 in Phase 1.
|
||||
|
||||
The artifacts shipped by 01-02 are clean — no TODOs, no stubs, no hardcoded empty data, no out-of-scope additions (`pl-safe` / `pr-safe`), no `tailwind.config.*` introduced, no service worker, no new dependencies.
|
||||
|
||||
### Human Verification Required
|
||||
|
||||
See frontmatter `human_verification` section. Three items, two strictly required for ROADMAP Phase 1 SC #4 (real iPhone install + real Android install) and one optional smoke test (live dev server view-source + CSS bundle inspection). All three are unchanged from the initial verification — they document real-device install behavior that no static check can confirm. The new `.pt-safe` / `.pb-safe` runtime rendering has been confirmed in the production CSS bundle artifact during this re-verification; the live dev-server check is a small extension of the existing item, not a new gate.
|
||||
|
||||
### Gaps Summary
|
||||
|
||||
**No gaps remaining.** The single gap from the initial verification (PWA-04 / Truth #3 — safe-area utility availability) was closed by `01-02-PLAN.md`, executed in commits `dff0264` (utility blocks) and `a293a4f` (summary). All four observable truths now verify; all four Phase 1 requirements (PWA-01..04) trace to claiming plans and verified artifacts; production build and type check both exit 0; the compiled CSS bundle contains the new rules.
|
||||
|
||||
The phase status moves from `gaps_found` to `human_needed` because real-device install verification (ROADMAP SC #4) is the only outstanding item — and that was always going to require human testing.
|
||||
|
||||
### Phase Boundary Restored
|
||||
|
||||
Phase 2's contract (SHELL-05, SHELL-06) only mandates **consumption** of safe-area padding via the available utility — Phase 2 can now write `<header class="sticky top-0 pt-safe ...">` and `<nav class="fixed bottom-0 pb-safe ...">` without inventing the utility itself. The ROADMAP boundary that PWA-04 made wobbly is now solid.
|
||||
|
||||
---
|
||||
|
||||
_Re-verified: 2026-05-03T18:05:00Z_
|
||||
_Verifier: Claude (gsd-verifier)_
|
||||
_Previous verification: 2026-05-03 (status: gaps_found, score: 3/4)_
|
||||
Loading…
Add table
Add a link
Reference in a new issue