docs(06-03): complete mobile analyzer detail page plan summary

- Documents title/company omission from identity block (D-25/D-36 conflict resolution)
- Documents IDOR posture T-06P03-02 as accept-and-flag with STATE.md follow-up recommendation
- Confirms no desktop files touched, TypeScript clean
This commit is contained in:
lorentz 2026-05-03 21:37:50 -04:00
parent aa4ff00065
commit cddf9cac8c

View file

@ -0,0 +1,116 @@
---
phase: 06-analyzer-feed-new
plan: "03"
subsystem: mobile-ui
tags: [mobile, analyzer, detail-page, read-only, segment-route]
dependency_graph:
requires:
- phase: 06-02
provides: AnalyzerStagePips and ConfidenceBadge components imported by identity block
- phase: 06-01
provides: GET /api/mobile/analyzer/feed endpoint (feed rows link to this detail page)
provides:
- /mobile/analyzer/[id] detail page — per-analysis summary view (ANL-03, ANL-04, ANL-05)
affects: []
tech-stack:
added: []
patterns: [use(params)-async-segment, useEffect-fetch-pattern, skeleton-then-content, read-only-enforcement]
key-files:
created:
- app/mobile/analyzer/[id]/page.tsx
modified: []
key-decisions:
- "Title and company name omitted from identity block — PersistedAnalysis does not include those fields (D-25/D-36: endpoint reused as-is, desktop routes unchanged)"
- "IDOR risk T-06P03-02 accepted and flagged: existing endpoint authenticates but does not scope by kiosk_settings; inherited product-level risk, not made worse by this plan"
- "Separate notFound state from generic error state to render distinct 404 vs error UX"
requirements-completed: [ANL-03, ANL-04, ANL-05]
duration: ~10min
completed: 2026-05-04
---
# Phase 06 Plan 03: Mobile Analyzer Detail Page Summary
**Read-only mobile detail page at `/mobile/analyzer/[id]` rendering Summary / Next Step / Next Step Rationale from the existing analyses endpoint, with identity block (stage pips + confidence badge) and "View full analysis" desktop link.**
## Performance
- **Duration:** ~10 min
- **Started:** 2026-05-04T00:00:00Z
- **Completed:** 2026-05-04
- **Tasks:** 1
- **Files modified:** 1
## Accomplishments
- Created `app/mobile/analyzer/[id]/page.tsx` — a real Next.js segment route (shareable URL per D-18) that taps an existing feed row and lands in a single navigation step.
- Three clearly labelled content sections (Summary / Next Step / Next Step Rationale) with `text-sm font-semibold` headings and `text-sm font-normal leading-relaxed whitespace-pre-wrap` body text; locked null fallback copy for absent fields.
- Identity block: ticket# mono badge, completed-at relative time, `AnalyzerStagePips`, `ConfidenceBadge`, optional "Review" pill — all using the Wave 2 components without duplication.
- In-page header with back chevron (`router.back()`), breadcrumb, and `ExternalLink` icon to desktop; footer "View full analysis" link with `min-h-[44px]` touch target.
- Read-only enforcement (ANL-05): zero `<form>`, zero `onSubmit`, zero edit/re-run/cancel/share controls — only navigation interactions.
- Loading skeleton (identity block + 3 sections), 404 empty-card state, generic error state with `toast.error`.
## Task Commits
1. **Task 1: Build the mobile analyzer detail page** - `aa4ff00` (feat)
## Files Created/Modified
- `app/mobile/analyzer/[id]/page.tsx` — Mobile per-analysis detail page; `'use client'`, segment route, reads `GET /api/analyzer/analyses/[id]`, renders three sections + identity block + footer link
## Decisions Made
### D-25/D-36 Conflict Resolution: Title and Company Name Omitted
`06-UI-SPEC.md §"Detail Page Identity Block"` lists `[title]` and `[company name]` fields. However:
- `PersistedAnalysis` (returned by `GET /api/analyzer/analyses/[id]`) does NOT include `title` or `companyName` — those live in the `tickets` and `companies` tables, and the existing endpoint does not join them.
- D-25 mandates reusing the existing endpoint as-is.
- D-36 prohibits modifying desktop analyzer routes (`app/api/analyzer/analyses/[id]/route.ts`) without user approval.
**Resolution:** Title and company name lines are omitted from the identity block. The breadcrumb (`Analyzer / #T20250034`) plus the prominent ticket# mono badge in the identity block convey sufficient ticket identity. A manager who needs full context taps "View full analysis" to reach the desktop page which joins all fields. This is explicitly called out in the plan's `<interfaces>` note as the correct resolution.
### IDOR Posture (T-06P03-02): Accept-and-Flag
The existing `GET /api/analyzer/analyses/[id]` endpoint:
- Authenticates the caller via `requireAuth()` (defense-in-depth; middleware also requires a session)
- Does NOT scope results by `kiosk_settings` company filter (unlike the Plan 06-01 feed endpoint which applies `getMobileCompanyFilter()`)
This means a logged-in user who guesses or constructs a valid `analyzer_analyses` UUID could retrieve an analysis for a ticket in a company outside their kiosk scope. This is an EXISTING product-level risk in the desktop analyzer (the same endpoint powers the desktop `/analyzer/analysis/[id]` page). Plan 06-03 does NOT make this worse — it simply exposes the same endpoint to mobile callers.
**Disposition:** Accept-and-flag. Practical risk is low (guessing a 36-character UUID v4 is computationally infeasible), but the architectural posture is weaker than the feed endpoint. Recommendation: post-Phase-6, add a `kiosk_settings` company scope check to `app/api/analyzer/analyses/[id]/route.ts` (or a mobile-specific wrapper endpoint). This should be tracked as a follow-up item in STATE.md.
## Deviations from Plan
None — plan executed exactly as written. The implementation matches the specification in `06-03-PLAN.md` including the identity block adjustment documented in `<interfaces>`.
## Issues Encountered
- **Worktree base mismatch:** The worktree was initialized on master (`db375fb`) rather than the Wave 2 base (`86369bd`). Resolved by `git reset --soft 86369bd... && git checkout HEAD -- .` before writing any code. Wave 2 components (`AnalyzerStagePips`, `ConfidenceBadge`) were then present and importable.
## User Setup Required
None — no external service configuration required. The page reads from an existing authenticated endpoint.
## Next Phase Readiness
- `/mobile/analyzer/[id]` is wired to the feed page (`AnalyzerFeedRow` links to this route via `href="/mobile/analyzer/${row.id}"`).
- BottomNav active-tab detection uses `pathname.startsWith('/mobile/analyzer')` (Phase 2) — the `[id]` segment inherits correct Analyzer tab highlight automatically.
- Wave 3 (Plan 06-03) is the final plan in Phase 6. No further plans in this phase.
## Known Stubs
None. All three content sections (`summary`, `nextStep`, `nextStepRationale`) fetch from the live endpoint. The locked null fallbacks ("Summary not available." etc.) are intentional UI copy for absent data, not content stubs.
## Threat Surface Scan
No new network endpoints, auth paths, file access patterns, or schema changes introduced. The page reads from `GET /api/analyzer/analyses/[id]` which is an existing authenticated endpoint. The IDOR posture concern (T-06P03-02) is an inherited risk documented above under Decisions Made — not a new surface introduced by this plan.
## Self-Check: PASSED
- `app/mobile/analyzer/[id]/page.tsx` — FOUND
- Commit `aa4ff00` — FOUND (`feat(06-03): add mobile analyzer detail page /mobile/analyzer/[id]`)
- `npx tsc --noEmit --pretty` — exits 0 (verified)
- No modifications to `app/api/analyzer/`, `app/analyzer/`, `lib/services/analyzer/` — confirmed via `git status --porcelain` (0 matches)
---
*Phase: 06-analyzer-feed-new*
*Completed: 2026-05-04*