Phase 1: Discovery & Baseline Capture Generated: 2026-02-10 Status: In Progress
| Dependency | Version | Notes |
|---|---|---|
| Next.js | 15.5.9 | App Router only, no Pages Router |
| React | 18.x | |
| React DOM | 18.x | |
| TypeScript | 5.8.3 (root), 5.x (frontend) | |
| Node.js types | ^20 | |
| ESLint | 8.57.1 | eslint-config-next 14.0.3 |
| Prettier | 3.6.2 |
| Setting | Frontend | Root |
|---|---|---|
strict |
true | true |
target |
es5 | ES2022 |
module |
esnext | commonjs |
jsx |
preserve | — |
incremental |
true | — |
isolatedModules |
true | — |
skipLibCheck |
true | true |
reactStrictMode: true
typescript.ignoreBuildErrors: true ← TEMPORARY, masks prop mismatches
eslint.ignoreDuringBuilds: false
experimental.scrollRestoration: true
images.unoptimized: true ← No next/image optimization
webpack: fs/net/tls set to false (Node polyfill suppression)
Key issues:
ignoreBuildErrors: true— suppresses TypeScript errors at build timestrict: falsein frontend tsconfig — weaker type checkingimages.unoptimized: true— no automatic image optimization
- Framework: Tailwind CSS 3.3.0 with PostCSS + Autoprefixer
- Dark mode: Class-based via
next-themes(darkMode: ["class"]) - Design system: CSS custom properties (HSL format) + Tailwind theme extension
- Component styling: Utility-first with
clsx,class-variance-authority,tailwind-merge - Plugins:
tailwindcss-animate,@tailwindcss/forms,@tailwindcss/typography
| Token | HSL Value | Purpose |
|---|---|---|
--background |
40 33% 98% | Page background |
--foreground |
220 30% 12% | Primary text |
--primary |
205 85% 28% | Deep Ocean — primary actions |
--primary-foreground |
40 30% 98% | Text on primary |
--secondary |
35 40% 92% | Warm Sand |
--accent |
38 70% 50% | Compass Brass |
--muted |
40 20% 94% | Subdued backgrounds |
--muted-foreground |
220 15% 45% | Secondary text |
--destructive |
0 72% 51% | Errors/danger |
--border |
40 20% 88% | Border color |
--ring |
205 85% 28% | Focus rings |
--radius |
0.625rem | Border radius base |
--ocean-deep |
205 85% 20% | Extended palette |
--ocean-mid |
205 75% 35% | Extended palette |
--ocean-light |
200 65% 55% | Extended palette |
--brass |
38 70% 50% | Brass accent |
--brass-dark |
35 65% 40% | Brass dark |
--parchment |
40 33% 96% | Parchment background |
--ink |
220 30% 15% | Deep text |
--success |
158 64% 40% | Success states |
--warning |
38 92% 50% | Warning states |
--chart-grid |
205 30% 85% | Chart gridlines |
| Token | HSL Value |
|---|---|
--background |
220 35% 8% |
--foreground |
40 20% 92% |
--primary |
200 70% 55% |
--primary-foreground |
220 35% 8% |
--secondary |
220 25% 18% |
--accent |
38 75% 55% |
--muted |
220 25% 15% |
--muted-foreground |
220 15% 60% |
--destructive |
0 65% 55% |
--border |
220 25% 20% |
--ocean-deep |
205 70% 50% |
--ocean-mid |
200 65% 45% |
--ocean-light |
195 60% 60% |
--brass |
38 75% 55% |
--brass-dark |
35 70% 45% |
--parchment |
40 15% 15% |
--ink |
40 20% 92% |
--success |
158 55% 50% |
--warning |
38 85% 55% |
--chart-grid |
220 20% 25% |
| Role | Font Family | Weights |
|---|---|---|
| Display (headings) | Libre Baskerville, Georgia, serif | 400, 700, 400i |
| Body | Source Sans 3, system-ui, sans-serif | 300, 400, 500, 600, 700 |
Font loading: Google Fonts → Migrated to @import url() in globals.cssnext/font/google in Phase 2 (self-hosted, non-blocking, CSS variables --font-heading / --font-body).
Heading scale:
- h1:
text-4xl md:text-5xl lg:text-6xl - h2:
text-3xl md:text-4xl - h3:
text-xl md:text-2xl
| Name | Value |
|---|---|
maritime |
0 4px 20px -2px hsl(var(--primary) / 0.15) |
maritime-lg |
0 20px 40px -15px hsl(var(--primary) / 0.2) |
brass |
0 4px 14px 0 hsl(var(--brass) / 0.35) |
card |
0 1px 3px hsl(--foreground/0.05), 0 1px 2px -1px hsl(--foreground/0.05) |
card-hover |
0 20px 40px -15px hsl(--primary/0.15), 0 8px 16px -8px hsl(--foreground/0.1) |
inner-light |
inset 0 1px 0 0 hsl(var(--background) / 0.5) |
Tailwind keyframes (12):
accordion-down, accordion-up, fade-in-up, fade-in, slide-in-right, slide-in-left, float, compass-needle, wave-shift, pulse, pulse-glow, shimmer, scale-in
CSS keyframes in globals.css (5 additional):
skeleton-shimmer, wave-shift, compass-needle, float-gentle, fade-in-up, pulse-glow
Framer Motion: framer-motion ^12.23.6 installed — usage appears minimal
.chart-grid/.chart-grid-dense— Nautical chart grid backgrounds.parchment-texture— SVG noise overlay.glass/.glass-heavy— Glassmorphism with backdrop-blur.card-nautical— Card with primary top border accent.card-hover— Elevated hover card state.btn-maritime/.btn-primary/.btn-secondary/.btn-brass/.btn-ghost— Maritime button variants.badge-*— Status badges (primary, success, warning, error, brass).skeleton— Shimmer loading skeleton.section-hero/.section-alt/.section-ocean— Section background variants.divider-nautical— Gradient divider with centered content
| Component | Radix Primitive |
|---|---|
| alert | — |
| badge | — |
| button | — |
| calendar | — (react-day-picker) |
| card | — |
| checkbox | @radix-ui/react-checkbox |
| date-picker | — (custom) |
| dialog | @radix-ui/react-dialog |
| input | — |
| label | @radix-ui/react-label |
| optimized-image | — (custom) |
| popover | @radix-ui/react-popover |
| progress | @radix-ui/react-progress |
| radio-group | @radix-ui/react-radio-group |
| responsive-card | — (custom) |
| scroll-area | @radix-ui/react-scroll-area |
| select | @radix-ui/react-select |
| skeleton | — |
| slider | @radix-ui/react-slider |
| switch | @radix-ui/react-switch |
| table | — |
| tabs | @radix-ui/react-tabs |
| textarea | — |
| theme-toggle | — (custom) |
| toaster | @radix-ui/react-toast |
react-checkbox, react-dialog, react-dropdown-menu, react-label, react-popover, react-progress, react-radio-group, react-scroll-area, react-select, react-slider, react-switch, react-tabs, react-toast
Admin: AdminOverview, AgentMonitoring, AnalyticsReports, CodeEditor, RevenueMetrics, SystemHealth, UserManagement Analytics: AnalyticsChart, AnalyticsDashboard Billing: StripeCheckout Charts: TideChart, WeatherChart Fleet: AddVesselDialog, CreateFleetDialog, CrewList, FleetAnalyticsDashboard, FleetVesselCard, InviteCrewDialog, SharePassageDialog Location: LocationAutocomplete, PortSelector Maps: InteractiveMap, RouteMap, WeatherOverlay, PassageMap Layout: Header, MobileNav Monitoring: AgentHealthDashboard Onboarding: OnboardingFlow, BoatSetupStep, CompletionStep, PreferencesStep, TutorialStep, WelcomeStep Export: ExportDialog, PDFPreview Passages: RecentPassages PWA: InstallPrompt Weather: WeatherWidget Other: ErrorBoundary, FeedbackWidget, DemoPassage
Single store: useStore with persist middleware (localStorage)
State slices:
- Passage Plans:
passagePlans[],currentPlanId, CRUD operations (keeps last 5) - Chat:
messages[](keeps last 50),addMessage,clearMessages - Map:
viewport(lat/lng/zoom),layers[]with toggle/add/remove - Preferences: theme, units, mapStyle, language, timezone
- Offline Queue:
offlineQueue[], add/clear operations - Session:
lastSynctimestamp
Selectors: useCurrentPlan(), useVisibleLayers(), useOfflineMode()
Persistence: Selective — persists plans, preferences, viewport, layers (without data), offline queue
Used for server state management alongside Zustand for client state.
DevTools included (@tanstack/react-query-devtools).
Real-time updates between frontend and orchestrator via SocketContext.
| Pattern | Usage |
|---|---|
React Query (useQuery/useMutation) |
Server state, API calls |
| Socket.io | Real-time passage planning progress, agent status |
| Supabase client | Auth, direct DB queries |
| Fetch API | Some direct API calls |
| Route | Page |
|---|---|
/ |
Landing page |
/login |
Authentication |
/signup |
Registration |
/reset-password |
Password reset |
/pricing |
Pricing page |
/api-docs |
API documentation |
/offline |
Offline mode page |
| Route | Page |
|---|---|
/dashboard |
User dashboard |
/planner |
Main passage planner |
/passages |
Saved passages list |
/passages/[id] |
Passage detail view |
/fleet |
Fleet management |
/weather |
Weather overview |
/onboarding |
New user onboarding |
/demo |
Demo experience |
| Route | Page |
|---|---|
/admin |
Admin overview |
/admin/agents |
Agent monitoring |
/admin/analytics |
Analytics reports |
- New User Signup —
/signup→ email verification →/onboarding→/dashboard - Passage Planning —
/planner→ enter departure/arrival → real-time agent processing → view results with maps/charts → export - Fleet Management —
/fleet→ create fleet → add vessels → invite crew → share passages - Weather Monitoring —
/weather→ view forecasts → overlay on map → check tidal data - Admin Monitoring —
/admin→ system health → agent status → analytics → user management
Total files with 'use client' directive: 72
Breakdown:
- Page components: 15 of 18 route pages
- UI primitives: 12 (dialog, select, label, checkbox, radio-group, calendar, toaster, popover, switch, tabs, theme-toggle, optimized-image)
- Domain components: ~40 (maps, charts, forms, admin panels)
- Contexts: 3 (providers, SocketContext, AuthContext)
- Layout: 1 (dashboard/layout)
Impact: Heavy client-side rendering. Most pages are fully client-rendered despite using App Router. Server Components are underutilized.
| Package | Version | Bundle Impact | Notes |
|---|---|---|---|
| leaflet + react-leaflet | 1.9.4 / 4.2.1 | ~150KB | Maps — no dynamic import observed |
| recharts | 3.1.0 | ~180KB | Charts — client only |
| framer-motion | 12.23.6 | ~120KB | Minimal usage, heavy import |
| html2canvas | 1.4.1 | ~200KB | PDF export |
| jspdf | 3.0.1 | ~300KB | PDF export |
| react-syntax-highlighter | 15.6.1 | ~80KB | API docs code blocks |
| d3 | 7.9.0 | ~250KB | Full D3 bundle (root dep) |
| @sentry/nextjs | 10.27.0 | ~100KB | Error tracking |
| @supabase/supabase-js | 2.51.0 | ~50KB | Auth & DB |
| socket.io-client | 4.8.1 | ~50KB | Real-time |
| Service | Purpose | Env Vars |
|---|---|---|
| Supabase | Auth + PostgreSQL + RLS | NEXT_PUBLIC_SUPABASE_* |
| Redis/Upstash | Caching, rate limiting | REDIS_URL |
| Stripe | Payments, subscriptions | STRIPE_* |
| NOAA | Primary weather data | NOAA_API_KEY |
| OpenWeather | Weather backup | OPENWEATHER_API_KEY |
| Sentry | Error tracking | SENTRY_DSN |
| Resend | Transactional email | RESEND_API_KEY |
| Google Maps | Location autocomplete | NEXT_PUBLIC_GOOGLE_MAPS_API_KEY |
- Frontend: Jest 30.0.4, jsdom environment, 85% coverage threshold
- Orchestrator/Agents: Jest 29.7.0
- Testing Library:
@testing-library/react16.3.0
- Config exists at
tests/playwright.config.ts - Missing:
global-setup.tsandglobal-teardown.ts(referenced but don't exist) - 7 browser projects configured (Chromium, Firefox, WebKit, Mobile Chrome/Safari, Edge, Chrome)
- 2 existing spec files:
auth.spec.ts,passage-planning.spec.ts @playwright/testnot installed as devDependency (needs to be added)
webpack-bundle-analyzer4.10.2 installed as frontend devDependency- Not wired into next.config.js — needs
ANALYZEenv var setup
Captured: 2026-02-10, dev server on localhost:3000
Shared JS (loaded on every page): 103 KB
chunks/3131-*.js— 46 KBchunks/c7879cf7-*.js— 54.2 KB- Other shared chunks — 3.03 KB
- Middleware — 33.9 KB
Heaviest Pages (First Load JS):
| Route | Page JS | First Load JS |
|---|---|---|
/planner |
13.5 KB | 406 KB |
/api-docs |
233 KB | 405 KB |
/admin |
22.7 KB | 364 KB |
/fleet |
15.8 KB | 325 KB |
/passages/[id] |
15.7 KB | 327 KB |
/admin/analytics |
5.4 KB | 320 KB |
/weather |
5.73 KB | 206 KB |
/passages |
5.76 KB | 202 KB |
/onboarding |
15.3 KB | 188 KB |
/dashboard |
9.48 KB | 185 KB |
/ (landing) |
13.3 KB | 184 KB |
/admin/agents |
8.32 KB | 179 KB |
/signup |
10.7 KB | 178 KB |
/login |
8.15 KB | 176 KB |
/pricing |
9.19 KB | 174 KB |
/reset-password |
5.28 KB | 173 KB |
Bundle analyzer report: frontend/analyze/client.html
| Metric | Score |
|---|---|
| Performance | 37 |
| Accessibility | 81 |
| Best Practices | 96 |
| SEO | 100 |
| Web Vital | Value |
|---|---|
| LCP (Largest Contentful Paint) | 19.4 s |
| CLS (Cumulative Layout Shift) | 0.001 |
| FCP (First Contentful Paint) | 3.1 s |
| TBT (Total Blocking Time) | 2,060 ms |
| Speed Index | 5.3 s |
Key findings:
- LCP of 19.4s is extremely slow (target: <2.5s) — likely caused by render-blocking Google Fonts
@import+ heavy client-side JS - TBT of 2,060ms indicates too much main-thread JavaScript execution
- FCP of 3.1s is above the 1.8s "good" threshold
- CLS is excellent (0.001) — layouts are stable
- SEO is perfect (100)
- Accessibility at 81 has room for improvement
Lighthouse reports: lighthouse-reports/landing.report.{html,json}
- Total routes captured: 16 / 16
- Breakpoints per route: 4 (mobile 375px, tablet 768px, laptop 1024px, desktop 1440px)
- Total screenshots: 64
- Location:
tests/e2e/screenshots/baseline/ - Notes:
/offlinereturned 500 — error state captured as baseline/weather,/onboarding, admin routes experienced navigation timeouts (no backend running) — partial load states captured- Auth-required routes show redirect-to-login behavior as expected
- TypeScript
strict: falsein frontend — weaker type safety ignoreBuildErrors: true— masks TypeScript errors at build- Google Fonts
@import— render-blocking, should usenext/font images.unoptimized: true— no image optimization- 72
'use client'files — underutilized Server Components - Full D3 import —
d3 ^7.9.0in root deps, heavy bundle - No code splitting for heavy libs (leaflet, recharts, html2canvas, jspdf)
- Missing Playwright infrastructure — global-setup/teardown don't exist
- Bundle analyzer not wired — installed but not configured
- Duplicate dependencies —
tailwind-mergein both root and frontend
Completed: 2026-02-11 Status: Complete
| Step | Change | Files |
|---|---|---|
| 1 | Migrated font loading from @import url() to next/font/google |
layout.tsx, globals.css, tailwind.config.ts |
| 2 | Added loading.tsx skeleton screens to 5 heavy routes |
planner, admin, fleet, dashboard, api-docs |
| 3 | Code-split 6 admin tab components via next/dynamic |
LazyComponents.tsx, admin/page.tsx |
| 4 | Lazy-loaded FleetAnalytics (already had wrapper) | fleet/page.tsx |
| 5 | Dynamic import for react-syntax-highlighter + vscDarkPlus theme | api-docs/page.tsx |
| 6 | Replaced framer-motion with CSS animate-slide-in-right |
OnboardingFlow.tsx, package.json |
| 7 | Removed unused socket.io-client dependency | package.json |
| 8 | Lazy-loaded DemoPassage on dashboard | dashboard/page.tsx |
| Route | Before | After | Change |
|---|---|---|---|
/admin |
364 KB | 178 KB | -51% |
/api-docs |
405 KB | 181 KB | -55% |
/fleet |
325 KB | 219 KB | -33% |
/passages/[id] |
327 KB | 328 KB | — |
/dashboard |
185 KB | 177 KB | -4% |
/onboarding |
188 KB | 188 KB | — |
/planner |
406 KB | 407 KB | — (map already dynamic) |
| Shared JS | 103 KB | 104 KB | +1 KB |
framer-motion^12.23.6 (~25KB gzipped) — replaced with Tailwind CSS animationsocket.io-client^4.8.1 (~45KB gzipped) — unused, WebSocket is native
- #3 Google Fonts
@import→ migrated tonext/font/google(self-hosted, non-blocking) - #7 No code splitting → admin, fleet analytics, api-docs syntax highlighter, demo passage all lazy-loaded
- #6 framer-motion → removed entirely, CSS replacement
Before: Render-blocking @import url() in globals.css → browser fetches external CSS → discovers font URLs → downloads fonts → renders text. Primary cause of 19.4s LCP.
After: next/font/google in layout.tsx self-hosts fonts at build time, applies font-display: swap, sets CSS variables --font-heading and --font-body. Zero external font requests at runtime.
Completed: 2026-02-11 Status: Complete
| Step | Change | Files |
|---|---|---|
| 1 | Dynamic import passageToGPX and generatePassagePDF at call site in planner |
planner/page.tsx |
| 2 | Lazy-load ExportDialog via next/dynamic with ssr: false |
passages/[id]/PassageDetailClient.tsx |
| 3 | Dynamic import all 4 export libs (gpx, kml, csv, pdf) inside handleExport switch |
components/export/ExportDialog.tsx |
| 4 | Added optimizePackageImports for lucide-react, date-fns, recharts, 13 @radix-ui packages |
next.config.js |
| 5 | Removed 3 unused dependencies | package.json |
| Route | Before (Phase 2) | After (Phase 3) | Change |
|---|---|---|---|
/planner |
407 KB | 230 KB | -43% |
/passages/[id] |
328 KB | 126 KB | -62% |
/admin/analytics |
322 KB | 322 KB | — |
/fleet |
219 KB | 219 KB | — |
/weather |
206 KB | 206 KB | — |
/passages |
202 KB | 203 KB | — |
/admin |
178 KB | 178 KB | — |
/api-docs |
181 KB | 181 KB | — |
/dashboard |
177 KB | 177 KB | — |
| Shared JS | 104 KB | 104 KB | — |
@googlemaps/js-api-loader^2.0.2 — maps use Leaflet, zero importsnext-auth^4.24.12 — auth uses Supabase exclusively, zero importsdetect-node-es^1.1.0 — zero imports
Dynamic imports (Steps 1-3): jspdf (~60KB) + html2canvas (~120KB) + export libs now load on-demand only when user clicks export buttons. Previously loaded statically on every page visit.
optimizePackageImports (Step 4): Next.js transforms barrel imports from lucide-react (292 icons), date-fns, recharts, and @radix-ui into direct file imports at build time, eliminating unused exports.
- #7 (partial) No code splitting for html2canvas, jspdf → now dynamically imported at call sites
- Unused dependencies (@googlemaps/js-api-loader, next-auth, detect-node-es) removed
| Route | Phase 1 Baseline | Phase 3 Result | Total Reduction |
|---|---|---|---|
/planner |
406 KB | 230 KB | -43% |
/passages/[id] |
327 KB | 126 KB | -61% |
/admin |
364 KB | 178 KB | -51% |
/api-docs |
405 KB | 181 KB | -55% |
/fleet |
325 KB | 219 KB | -33% |
/admin/analytics |
320 KB | 322 KB | — |
Completed: 2026-02-11 Status: Complete
| Step | Change | Files |
|---|---|---|
| 1 | Dynamic import AnalyticsDashboard (recharts: 6 chart types) with skeleton fallback |
admin/analytics/page.tsx |
| 2 | Lazy-load ReactQueryDevtools via next/dynamic, conditionally render in dev only |
providers.tsx |
| 3 | Converted landing page to Server Component (removed 'use client') |
page.tsx |
| Route | Before (Phase 3) | After (Phase 4) | Change |
|---|---|---|---|
/admin/analytics |
322 KB | 179 KB | -44% |
/ (landing) |
185 KB | 177 KB | -4% |
/admin |
178 KB | 178 KB | — |
/planner |
230 KB | 230 KB | — |
/passages/[id] |
126 KB | 126 KB | — |
| Shared JS | 104 KB | 104 KB | — |
AnalyticsDashboard lazy-load (Step 1): The analytics page statically imported AnalyticsDashboard which pulls in 6 recharts chart types (AreaChart, BarChart, LineChart, PieChart + axes/tooltips). Now loads on-demand with skeleton placeholder. Only admin users ever visit this page.
ReactQueryDevtools (Step 2): Previously imported unconditionally in the shared provider bundle. Now dynamically imported and only rendered in development (process.env.NODE_ENV === 'development'). Tree-shaken from production builds.
Landing page Server Component (Step 3): Removed unnecessary 'use client' directive from the landing page. The page has zero hooks or event handlers — all interactive children (Header, Button) already have their own client boundaries. This allows Next.js to server-render the page and send less JavaScript.
| Route | Phase 1 Baseline | Phase 4 Result | Total Reduction |
|---|---|---|---|
/admin/analytics |
320 KB | 179 KB | -44% |
/admin |
364 KB | 178 KB | -51% |
/api-docs |
405 KB | 181 KB | -55% |
/planner |
406 KB | 230 KB | -43% |
/passages/[id] |
327 KB | 126 KB | -62% |
/fleet |
325 KB | 219 KB | -33% |
/ (landing) |
184 KB | 177 KB | -4% |
All routes now under 250 KB first-load JS. No route exceeds 230 KB.
Completed: 2026-02-11 Status: Complete
| Step | Change | Files |
|---|---|---|
| 1 | Lazy-load 4 fleet dialog components (CreateFleet, AddVessel, InviteCrew, SharePassage) | fleet/page.tsx |
| 2 | Enabled TypeScript strict: true with noImplicitAny: false |
tsconfig.json |
| 3 | Added explicit types field to prevent transitive type leakage from monorepo root |
tsconfig.json |
| Route | Before (Phase 4) | After (Phase 5) | Change |
|---|---|---|---|
/fleet |
219 KB | 183 KB | -16% |
| All other routes | — | — | unchanged |
Enabled: strict: true with noImplicitAny: false (deferred — ~50 implicit any annotations needed).
Strict checks now active:
strictNullChecks— catchesundefined/nullaccess (critical for safety code)strictFunctionTypes— stricter function parameter checkingstrictBindCallApply— type-safe bind/call/applystrictPropertyInitialization— uninitialized class properties flaggedalwaysStrict— emits"use strict"in all output
Remaining ~40 type errors (all caught by strictNullChecks, visible in editors):
AnalyticsReports.tsx:dateRangepossibly undefined (12 errors)api-docs/page.tsx: react-syntax-highlighter Prism type mismatch (6)AgentMonitoring.tsx/SystemHealth.tsx: array typing (6)reset-password/page.tsx: toast prop types (5)- Other: LocationAutocomplete window.google, AuthContext, TideChart, Sentry Replay (~11)
These are masked by ignoreBuildErrors: true in next.config.js. Recommended to fix incrementally, then remove ignoreBuildErrors.
| Route | Phase 1 Baseline | Phase 5 Result | Total Reduction |
|---|---|---|---|
/passages/[id] |
327 KB | 126 KB | -61% |
/api-docs |
405 KB | 181 KB | -55% |
/admin |
364 KB | 178 KB | -51% |
/fleet |
325 KB | 183 KB | -44% |
/admin/analytics |
320 KB | 179 KB | -44% |
/planner |
406 KB | 231 KB | -43% |
/ (landing) |
184 KB | 177 KB | -4% |
All routes now under 231 KB. Every route that was over 300 KB has been cut by 44-61%.
Date: 2026-02-11 Status: Complete
Fixed all ~40 remaining TypeScript strict mode errors (strictNullChecks, strictFunctionTypes, etc.) across 14 files. The frontend now compiles cleanly with strict: true + noImplicitAny: false.
| File | Error Type | Fix |
|---|---|---|
app/api/feedback/route.ts |
userId typed too narrowly |
Added explicit string | null type annotation |
app/components/admin/AgentMonitoring.tsx |
parts = [] inferred as never[] |
Added string[] type annotation |
app/components/admin/SystemHealth.tsx |
Same never[] inference |
Added string[] type annotation |
app/components/admin/AnalyticsReports.tsx |
dateRange?.from/.to possibly undefined |
Added optional chaining and null guards |
app/components/charts/TideChart.tsx |
Dot prop returned null (not valid SVG element) |
Return <circle r={0} /> instead of null |
app/components/location/LocationAutocomplete.tsx |
window.google not on Window type |
Added global Window interface declaration |
app/contexts/AuthContext.tsx |
session?.user?.id possibly undefined in .eq() |
Added null guard before Supabase query |
app/lib/export/gpx.ts |
parts = [] inferred as never[] |
Added string[] type annotation |
app/lib/performance.ts |
this implicitly typed as any in throttle |
Added explicit this: unknown parameter |
app/api-docs/page.tsx |
Dynamic import Prism type mismatch | Cast dynamic component with any typing |
app/reset-password/page.tsx |
toast({title, desc}) — wrong API for sonner |
Migrated to toast.error(msg, {description}) |
sentry.client.config.ts |
new Sentry.Replay() deprecated |
Updated to Sentry.replayIntegration() |
app/passages/[id]/PassageDetailClient.tsx |
Array.from(new Set(...)) returns unknown[] |
Cast result as string[] |
app/planner/page.tsx |
DatePicker setDate prop doesn't exist |
Changed to onDateChange (matching component API) |
- TypeScript errors: ~40 → 0
- Build: Passes cleanly
- Bundle sizes: Unchanged (no runtime impact)
tsc --noEmit: Zero errors
Date: 2026-02-11 Status: Complete
Enabled noImplicitAny: true and fixed 39 remaining implicit any errors across 6 files. Then removed ignoreBuildErrors: true from next.config.js — the build now enforces TypeScript type checking, catching regressions in CI.
tsconfig.json: noImplicitAny: false → noImplicitAny: true
next.config.js: ignoreBuildErrors: true → ignoreBuildErrors: false
| File | Errors Fixed | Fix Pattern |
|---|---|---|
app/passages/[id]/PassageDetailClient.tsx |
15 | Explicit any/number on .map()/.filter()/.sort()/.some() callbacks |
app/lib/export/csv.ts |
7 | Explicit any/number on .forEach()/.map() callbacks |
app/lib/export/gpx.ts |
5 | Explicit any/number on .forEach()/.map() callbacks |
app/lib/export/kml.ts |
5 | Explicit any/number on .forEach() callbacks |
app/components/fleet/FleetAnalyticsDashboard.tsx |
5 | Explicit any/number on .map() callbacks |
app/components/onboarding/steps/CompletionStep.tsx |
2 | keyof typeof cast for object key indexing |
Root cause: All shared types (Passage, FleetAnalytics, etc.) are aliased to any in app/types/shared.ts. When noImplicitAny is enabled, callback parameters on any[] arrays require explicit type annotations.
- TypeScript errors: 39 → 0
tsc --noEmit: Zero errors with fullstrict: truenpm run build: Passes with type checking enforced (noignoreBuildErrors)- Bundle sizes: Unchanged
The frontend now runs with full TypeScript strict mode:
strict: true(includesstrictNullChecks,strictFunctionTypes,strictBindCallApply,strictPropertyInitialization,noImplicitThis,alwaysStrict)noImplicitAny: trueignoreBuildErrors: false— CI will catch type regressions
Future improvement: Replace any type aliases in app/types/shared.ts with proper types from the shared package to get real type safety for Passage, FleetAnalytics, etc.
frontend/next.config.js— Next.js build configurationfrontend/tsconfig.json— Frontend TypeScript configfrontend/tailwind.config.ts— Tailwind theme and pluginsfrontend/postcss.config.js— PostCSS configurationfrontend/app/globals.css— Global styles and design tokenstests/playwright.config.ts— Playwright test configuration
Date: 2026-02-12 Status: Complete
Implemented 6 life-safety-critical fixes across the orchestrator and agent layers. These address audit logging, data freshness enforcement, failure messaging, weather worst-case warnings, and route detour verification.
| Step | Fix | Files Modified |
|---|---|---|
| 1 | Safety audit logging — SafetyAuditLogger integrated into orchestrator. Logs route analyses, data sources, and all warnings shown to users for incident investigation. Flushes pending writes on shutdown. | orchestrator/src/SimpleOrchestrator.ts |
| 2 | Weather freshness enforcement — New validateWeatherFreshness() rejects data >1hr old per CLAUDE.md, warns on approaching staleness (>30min), flags missing freshness metadata, propagates confidence levels to user. |
orchestrator/src/SimpleOrchestrator.ts |
| 3 | Tidal freshness validation — New validateTidalFreshness() in TidalAgent checks prediction coverage vs requested time range, detects gaps >6hrs, warns on distant stations (>40nm). Orchestrator extracts and surfaces tidal warnings. |
agents/tidal/src/index.ts, orchestrator/src/SimpleOrchestrator.ts |
| 4 | Enriched safety failure messaging — SafetyAgent crash no longer returns a single generic message. Now provides: failure reason/timestamp, 6-item manual safety checklist (NOAA warnings, depth verification, restricted areas, weather, tides, emergency contacts), 4 required actions (manual verification, official charts, float plan, VHF 16), systemFailure flag, and urgent disclaimer. |
orchestrator/src/SimpleOrchestrator.ts |
| 5 | Weather worst-case warnings — Strengthened generateWarnings() with gale warning (>33kt), moderate wind warning (>20kt), rough seas warning (>4m), and single-source confidence warning. Handles both array and object weather formats. Also includes windGust and significantWaveHeight fields. |
orchestrator/src/SimpleOrchestrator.ts |
| 6 | Route detour clearance verification — Legacy calculateDetour() replaced with iterative version that tries 10/20/30nm offsets on both sides, verifies both new segments clear all avoid areas using turf.booleanCrosses()/turf.booleanWithin(), and throws requiring manual route planning if no clear path found. |
agents/route/src/RouteAgent.ts |
| File | Lines Changed |
|---|---|
orchestrator/src/SimpleOrchestrator.ts |
+180 (audit logging, freshness checks, enriched fallbacks, tidal warning extraction, strengthened weather warnings) |
agents/tidal/src/index.ts |
+45 (tidal freshness validation, response enrichment) |
agents/route/src/RouteAgent.ts |
+60 (iterative clearance-verified detour calculation) |
- Build:
npm run build— all workspaces pass - Tests:
npm test— 669+ tests pass, 0 new failures - Type check:
npm run type-check— 0 errors (shared, orchestrator, frontend)
frontend/app/components/ui/— 25 shadcn/ui componentsfrontend/app/components/— 40+ domain componentsfrontend/app/store/— Zustand storefrontend/app/hooks/— Custom hooks (4: use-toast, useAnalytics, usePassagePlanner, useServiceWorker)frontend/app/contexts/— React contexts (SocketContext, AuthContext)frontend/app/lib/— Utility functionsshared/src/types/— Core types (boat, core, errors, fleet, passage, safety)shared/src/services/— Shared services (data-freshness, feature-flags, noaa-api-client, retry, resilience)