- Complete security, performance, and UX fix specifications - Zero-dependency patterns embedded (no local file references) - 15 agent assignments with specific tasks - Implementation patterns for JWT, auth, pagination, services - Marine CSS baseline, ARIA labels, lazy loading - Success criteria and testing checklist - Ready for cloud Claude Code session with GitHub-only access Source: Codex + Gemini comprehensive reviews + GPT-5 synthesis Target: Week 1 critical path to production readiness
19 KiB
NaviDocs Critical Fixes - Cloud Implementation Mission
GitHub Repository: https://github.com/dannystocker/navidocs
Branch: navidocs-cloud-coordination
Target: Premium boat management platform for €800K-€1.5M yachts
Mission Overview
You are a Sonnet instance managing 15 Haiku agents to implement critical security, performance, and UX fixes identified in comprehensive code reviews (Codex + Gemini). This is a 3-week sprint to production readiness.
Review Sources (on GitHub in branch navidocs-cloud-coordination):
HANDOVER_SESSION_2025-11-14.md- Full contextreviews/CODEX_SECURITY_ARCHITECTURE_REPORT.md- Security review (if exists)reviews/GEMINI_PERFORMANCE_UX_REPORT.md- Performance/UX review (if exists)
Reference Implementation Guide: The detailed implementation patterns are in the Windows downloads folder, but I'll embed the key patterns here for zero-dependency execution.
Critical Issues to Fix
🔴 BLOCKERS (Week 1 - Must Fix Before Production)
- JWT Secret Enforcement - Hard-coded default secret allows token forgery
- Auth Gaps - Document/search/upload/image/stats routes use
req.user?.id || 'test-user-id' - Meilisearch Token Fallback - Falls back to global search key on failure
- Bundle Size 2.3MB - No lazy loading, target <500KB gzipped
- Touch Targets 12-40px - Need 60×60px minimum for marine gloves
- No Pagination - 11
.all()queries without limits - Small Fonts - As low as 10px, need 24-48px for sunlight readability
- Missing ARIA Labels - 29 icon-only buttons fail WCAG
🟡 HIGH PRIORITY (Week 2)
- God Components -
DocumentView.vue(1386 lines),SearchView.vue(628 lines) - Service Layer - Business logic in routes, need service extraction
- Token Refresh - No automatic 401 handling
- Images Without Alt - Accessibility failures
- Legacy Auth Middleware - Two auth implementations cause confusion
Agent Assignment & Parallel Execution
Spawn 15 Haiku agents in PARALLEL using a single message with 15 Task tool calls:
Security Team (Agents 1-5)
Agent 1: JWT Secret Enforcement
- Create
server/config/env.jswith mandatoryJWT_SECRETvalidation - Update
server/services/auth.service.jsto use central config - Update
server/middleware/auth.middleware.jsto use central config - Remove or deprecate
server/middleware/auth.js(legacy) - Add startup assertion:
assert(JWT_SECRET && JWT_SECRET.length >= 32)
Agent 2: Document Route Auth
- Add
authenticateTokenmiddleware to all routes inserver/routes/documents.js - Remove all
req.user?.id || 'test-user-id'patterns - Use
req.user.userIddirectly (set by middleware) - Add
requireOrganizationMemberwhere organization scoping needed - Test: Verify 401 on unauthenticated requests
Agent 3: Search/Upload/Image Route Auth
- Secure
server/routes/search.js(both/tokenand/search) - Secure
server/routes/upload.jsand/upload/quick-ocr - Secure
server/routes/images.js(all image endpoints) - Remove
test-user-idfallbacks - Add organization membership checks
Agent 4: Stats & Jobs Auth
- Secure
server/routes/stats.jswithrequireSystemAdminfor global stats - Create
/stats/organizations/:organizationIdfor scoped stats - Secure
server/routes/jobs.jswith proper auth - Remove public access to operational metrics
Agent 5: Meilisearch Token Hardening
- Modify
server/routes/search.jsPOST/tokento fail closed - Only allow fallback in
NODE_ENV === 'development' - Log tenant token failures to ops team
- Return 500 with retry message in production on failure
Performance Team (Agents 6-9)
Agent 6: Lazy Loading Routes
- Update
client/src/router/index.js - Convert all direct imports to
() => import('./views/XYZ.vue') - Apply to: HomeView, DocumentView, LibraryView, SearchView, SettingsView
- Test: Verify bundle splits in
npm run build - Target: Reduce initial bundle from 2.3MB to <1MB
Agent 7: Pagination Utility
- Create
server/utils/pagination.jswithparsePagination(req, options) - Support
?page=1&pageSize=50query params - Max page size: 200, default: 50
- Return
{ page, pageSize, offset }for SQL queries - Add total count helper for responses
Agent 8: Apply Pagination to Routes
- Update
server/routes/documents.jsGET/with pagination - Update
server/routes/images.jslistings - Update
server/routes/jobs.jsGET/api/jobs - Replace all
.all()with.all(limit, offset)pattern - Return
{ data: rows, page, pageSize, total }format
Agent 9: Central API Client with Token Refresh
- Create
client/src/api/http.jsusing Axios - Add request interceptor to inject
Authorization: Bearer ${accessToken} - Add response interceptor for 401 handling
- Implement automatic refresh using
/api/auth/refresh - Prevent multiple concurrent refresh requests (use promise singleton)
Architecture Team (Agents 10-12)
Agent 10: Document Service Layer
- Create
server/services/document.service.js - Extract methods:
listForUser(),getForUser(),deleteForUser() - Use pagination support from Agent 7
- Enforce organization membership via JOIN
- Throw 404 errors with
.status = 404for service-level handling
Agent 11: Image Service Layer
- Create
server/services/image.service.js - Extract image retrieval, validation, path safety logic
- Move Meilisearch cleanup logic from routes
- Support pagination for image lists
Agent 12: Stats Service Layer
- Create
server/services/stats.service.js - Implement
getGlobalStats()(admin only) - Implement
getOrgStats(organizationId, userId)with membership check - Always join via
user_organizationsto prevent cross-tenant leaks
UX Team (Agents 13-15)
Agent 13: Marine CSS Baseline
- Create
client/src/assets/marine.css - Define CSS variables:
--nd-touch-target: 60px,--nd-font-base: 16px,--nd-font-large: 24px,--nd-font-xlarge: 32px - Apply to all
button,[role='button'],a.nav-link(min 60×60px) - Update
client/src/main.jsto import marine.css - Test: Verify all interactive elements meet 60px minimum
Agent 14: ARIA Labels & Alt Text
- Scan all
.vuefiles for icon-only buttons - Add
aria-labelto all interactive elements without visible text - Add
aria-hidden="true"to decorative icons inside labeled buttons - Add
:altattributes to all<img>tags - Use descriptive labels: "Delete item", "Close dialog", etc.
Agent 15: Typography & Contrast
- Update all font-size CSS < 16px to at least 16px base
- Create
.nd-metricclass for critical numbers (32-48px, font-weight: 700) - Apply to key metrics in dashboard/stats components
- Add
.nd-headingclass (24px, font-weight: 600) - Verify contrast ratios ≥7:1 for WCAG AAA (Navy #1E3A8A on White #FFF)
Implementation Patterns (Zero-Dependency Reference)
Pattern 1: JWT Secret Enforcement
// server/config/env.js
import assert from 'node:assert';
export const NODE_ENV = process.env.NODE_ENV || 'development';
export const JWT_SECRET = process.env.JWT_SECRET;
assert(
JWT_SECRET && JWT_SECRET.length >= 32,
'JWT_SECRET environment variable is required and must be at least 32 chars'
);
export const MEILISEARCH_HOST = process.env.MEILISEARCH_HOST;
export const MEILISEARCH_MASTER_KEY = process.env.MEILISEARCH_MASTER_KEY;
// server/services/auth.service.js
import jwt from 'jsonwebtoken';
import { JWT_SECRET } from '../config/env.js';
export function signAccessToken(payload, options = {}) {
return jwt.sign(payload, JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRES_IN || '15m',
...options,
});
}
Pattern 2: Auth Middleware with RBAC
// server/middleware/auth.middleware.js
import jwt from 'jsonwebtoken';
import { JWT_SECRET } from '../config/env.js';
export function authenticateToken(req, res, next) {
const header = req.headers['authorization'];
const token = header && header.split(' ')[1];
if (!token) return res.sendStatus(401);
jwt.verify(token, JWT_SECRET, (err, decoded) => {
if (err) return res.sendStatus(401);
req.user = decoded; // { userId, email, organizationIds, ... }
next();
});
}
export function requireOrganizationMember(req, res, next) {
const orgId = req.params.organizationId || req.body.organizationId;
if (!orgId) return res.status(400).json({ error: 'Organization ID required' });
const isMember = req.user.organizationIds?.includes(orgId);
if (!isMember) return res.status(403).json({ error: 'Not a member of this organization' });
next();
}
Pattern 3: Pagination Utility
// server/utils/pagination.js
export function parsePagination(req, { defaultSize = 50, maxSize = 200 } = {}) {
const page = Math.max(parseInt(req.query.page || '1', 10), 1);
const pageSize = Math.min(
Math.max(parseInt(req.query.pageSize || String(defaultSize), 10), 1),
maxSize
);
const offset = (page - 1) * pageSize;
return { page, pageSize, offset };
}
Pattern 4: Service Layer for Documents
// server/services/document.service.js
import db from '../db/db.js';
export function listForUser(userId, { limit, offset }) {
const rows = db.prepare(`
SELECT d.*
FROM documents d
JOIN user_organizations uo
ON uo.organization_id = d.organization_id
WHERE uo.user_id = ?
ORDER BY d.created_at DESC
LIMIT ? OFFSET ?
`).all(userId, limit, offset);
const { count } = db.prepare(`
SELECT COUNT(*) as count
FROM documents d
JOIN user_organizations uo
ON uo.organization_id = d.organization_id
WHERE uo.user_id = ?
`).get(userId);
return { rows, total: count };
}
export function getForUser(userId, documentId) {
const doc = db.prepare(`
SELECT d.*
FROM documents d
JOIN user_organizations uo
ON uo.organization_id = d.organization_id
WHERE d.id = ? AND uo.user_id = ?
`).get(documentId, userId);
if (!doc) {
const err = new Error('Document not found');
err.status = 404;
throw err;
}
return doc;
}
Pattern 5: Route Using Service + Pagination
// server/routes/documents.js
import { Router } from 'express';
import { authenticateToken } from '../middleware/auth.middleware.js';
import { parsePagination } from '../utils/pagination.js';
import * as documentService from '../services/document.service.js';
const router = Router();
router.get('/',
authenticateToken,
async (req, res, next) => {
try {
const { page, pageSize, offset } = parsePagination(req);
const { rows, total } = documentService.listForUser(
req.user.userId,
{ limit: pageSize, offset }
);
res.json({ data: rows, page, pageSize, total });
} catch (err) {
next(err);
}
}
);
router.get('/:id',
authenticateToken,
async (req, res, next) => {
try {
const doc = documentService.getForUser(req.user.userId, req.params.id);
res.json(doc);
} catch (err) {
next(err);
}
}
);
export default router;
Pattern 6: Lazy Loading Routes
// client/src/router/index.js
import { createRouter, createWebHistory } from 'vue-router';
const routes = [
{
path: '/',
name: 'home',
component: () => import('../views/HomeView.vue')
},
{
path: '/documents/:id',
name: 'document',
component: () => import('../views/DocumentView.vue'),
props: true
},
{
path: '/library',
name: 'library',
component: () => import('../views/LibraryView.vue')
},
{
path: '/search',
name: 'search',
component: () => import('../views/SearchView.vue')
}
];
export default createRouter({
history: createWebHistory(),
routes,
});
Pattern 7: Central API Client with Token Refresh
// client/src/api/http.js
import axios from 'axios';
import { useAuthStore } from '@/stores/auth';
const api = axios.create({
baseURL: '/api',
});
api.interceptors.request.use((config) => {
const auth = useAuthStore();
if (auth.accessToken) {
config.headers.Authorization = `Bearer ${auth.accessToken}`;
}
return config;
});
let refreshPromise = null;
api.interceptors.response.use(
(response) => response,
async (error) => {
const { response, config } = error;
const auth = useAuthStore();
if (response?.status === 401 && !config._retry && auth.refreshToken) {
config._retry = true;
if (!refreshPromise) {
refreshPromise = auth.refreshAccessToken();
}
try {
await refreshPromise;
refreshPromise = null;
config.headers.Authorization = `Bearer ${auth.accessToken}`;
return api(config);
} catch (err) {
refreshPromise = null;
auth.logout();
window.location.href = '/login';
}
}
return Promise.reject(error);
}
);
export default api;
Pattern 8: Marine CSS Baseline
/* client/src/assets/marine.css */
:root {
--nd-touch-target: 60px;
--nd-font-base: 16px;
--nd-font-large: 24px;
--nd-font-xlarge: 32px;
--nd-font-metric: 48px;
--nd-navy: #1E3A8A;
--nd-teal: #0D9488;
--nd-white: #ffffff;
}
body {
font-size: var(--nd-font-base);
line-height: 1.5;
}
/* All interactive elements */
button,
[role='button'],
a.nav-link,
.btn,
.icon-button {
min-width: var(--nd-touch-target);
min-height: var(--nd-touch-target);
padding: 0.75rem 1rem;
}
/* Typography scale */
.nd-heading {
font-size: var(--nd-font-large);
font-weight: 600;
}
.nd-metric {
font-size: var(--nd-font-metric);
font-weight: 700;
color: var(--nd-navy);
}
/* High contrast for marine use */
.nd-high-contrast {
background: var(--nd-navy);
color: var(--nd-white);
}
Pattern 9: ARIA Labels
<!-- BEFORE -->
<button @click="deleteItem">
<TrashIcon />
</button>
<!-- AFTER -->
<button
type="button"
aria-label="Delete item"
@click="deleteItem"
>
<TrashIcon aria-hidden="true" />
</button>
<!-- Images BEFORE -->
<img :src="result.thumbnail" class="thumbnail">
<!-- Images AFTER -->
<img
:src="result.thumbnail"
:alt="`${result.title} thumbnail`"
class="thumbnail"
/>
Execution Instructions
Step 1: Clone Repository
git clone https://github.com/dannystocker/navidocs.git
cd navidocs
git checkout navidocs-cloud-coordination
Step 2: Analyze Current State
# Find all .all() queries without limits
grep -r "\.all()" server/routes/ | wc -l
# Find test-user-id patterns
grep -r "test-user-id" server/routes/
# Find small touch targets
grep -r "width.*px\|height.*px" client/src/components/ | grep -E "width: [1-5][0-9]px|height: [1-5][0-9]px"
# Find small fonts
grep -r "font-size.*px" client/src/ | grep -E "[0-9]{1}px|1[0-5]px"
# Find missing ARIA labels
grep -r "<button" client/src/ | grep -v "aria-label"
Step 3: Spawn 15 Haiku Agents in Parallel
CRITICAL: Use a single message with 15 Task tool calls to spawn all agents simultaneously:
I'm spawning 15 Haiku agents in parallel to implement all critical fixes.
[Agent 1 Task tool call]
[Agent 2 Task tool call]
...
[Agent 15 Task tool call]
Each agent should:
- Read relevant files from GitHub
- Implement their assigned fixes
- Test changes locally
- Commit with descriptive message
- Report completion status
Step 4: Integration & Testing
After all 15 agents complete:
-
Build verification:
npm install npm run build du -sh dist/ # Verify <500KB gzipped target -
Security audit:
npm audit --production grep -r "test-user-id" server/ # Should be 0 results grep -r "JWT_SECRET.*||" server/ # Should be 0 results -
UX verification:
# All touch targets ≥60px grep -r "width.*px\|height.*px" client/src/components/ | grep -E "width: [1-5][0-9]px" | wc -l # Should be 0 # All fonts ≥16px grep -r "font-size.*px" client/src/ | grep -E "[0-9]{1}px|1[0-5]px" | wc -l # Should be 0 # All buttons have ARIA grep -r "<button" client/src/ | grep -v "aria-label" | wc -l # Should be minimal -
Manual testing:
- Start services:
npm run dev(client) +node server/index.js - Test unauthenticated access to
/api/documents→ should 401 - Test authenticated access → should work with pagination
- Verify bundle splits in Network tab
- Test touch targets with Chrome DevTools mobile view
- Verify search token refresh on 401
- Start services:
Step 5: Commit & Push
git add .
git commit -m "Implement critical security, performance, and UX fixes
- Enforce JWT_SECRET with mandatory validation
- Secure all document/search/upload/image/stats routes with auth
- Add pagination to all .all() queries (11 endpoints)
- Implement lazy loading for Vue routes (reduce bundle 50%)
- Add marine CSS baseline (60px touch targets, 16px+ fonts)
- Extract service layer for documents, images, stats
- Add central API client with automatic token refresh
- Add ARIA labels to 29 icon-only buttons
- Add alt text to all images
- Fail closed on Meilisearch tenant tokens
Reviews: CODEX + Gemini comprehensive audits
Agents: 15 Haiku agents in parallel execution
Week 1 critical path: Security + Performance + Marine UX
"
git push origin navidocs-cloud-coordination
Success Criteria
Week 1 Completion Checklist:
- JWT_SECRET is mandatory, no default fallback
- All 11 routes with
test-user-idnow require auth - All
.all()queries have pagination support - Vue routes use lazy loading
- Bundle size <1MB raw (target <500KB gzipped)
- All interactive elements ≥60×60px
- All fonts ≥16px base, 24-48px for metrics
- 29 icon-only buttons have ARIA labels
- All images have alt text
- Meilisearch tokens fail closed in production
- Service layer for documents, images, stats
- Central API client with token refresh
- npm audit shows 0 critical/high vulnerabilities
- Build succeeds without errors
- Manual testing passes on key flows
Performance Targets:
- Initial load time: <2s on 3G
- Bundle size: <500KB gzipped
- API response time: <200ms p95
- Touch target size: 100% compliance at ≥60px
- Font size: 100% compliance at ≥16px
Security Targets:
- 0 SQL injection vulnerabilities
- 0 unauthenticated access to sensitive routes
- 0 hardcoded secrets
- 0 npm audit critical/high issues
- JWT tokens require proper secret
Emergency Rollback
If issues occur:
git checkout navidocs-cloud-coordination
git reset --hard origin/navidocs-cloud-coordination
Review individual agent commits and cherry-pick working changes.
Post-Implementation
After Week 1 fixes are live:
Week 2 priorities:
- Decompose god components using composables
- Add loading skeletons
- Implement high-contrast "bridge mode"
- Add keyboard navigation shortcuts
- Database index optimization
Week 3 priorities:
- E2E testing with Playwright
- Performance monitoring
- Component library documentation
- Deployment automation
Notes for Sonnet Orchestrator
- Spawn all 15 agents in a SINGLE message using 15 Task tool calls
- Monitor agent completion and resolve conflicts
- Each agent must test their changes before committing
- Integration testing after all agents complete
- Git commits should reference agent number and feature
- Use descriptive commit messages with Before/After context
- Push to
navidocs-cloud-coordinationbranch only - Tag completion:
v1.0.0-security-ux-fixes
Ready to execute. Spawn 15 Haiku agents in parallel and implement all critical fixes.