Add comprehensive cloud implementation prompt for 15-agent parallel execution

- 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
This commit is contained in:
Danny Stocker 2025-11-14 16:59:43 +01:00
parent 9b46503484
commit e3a1105db4

View file

@ -0,0 +1,700 @@
# 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 context
- `reviews/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)
1. **JWT Secret Enforcement** - Hard-coded default secret allows token forgery
2. **Auth Gaps** - Document/search/upload/image/stats routes use `req.user?.id || 'test-user-id'`
3. **Meilisearch Token Fallback** - Falls back to global search key on failure
4. **Bundle Size 2.3MB** - No lazy loading, target <500KB gzipped
5. **Touch Targets 12-40px** - Need 60×60px minimum for marine gloves
6. **No Pagination** - 11 `.all()` queries without limits
7. **Small Fonts** - As low as 10px, need 24-48px for sunlight readability
8. **Missing ARIA Labels** - 29 icon-only buttons fail WCAG
### 🟡 HIGH PRIORITY (Week 2)
9. **God Components** - `DocumentView.vue` (1386 lines), `SearchView.vue` (628 lines)
10. **Service Layer** - Business logic in routes, need service extraction
11. **Token Refresh** - No automatic 401 handling
12. **Images Without Alt** - Accessibility failures
13. **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.js` with mandatory `JWT_SECRET` validation
- Update `server/services/auth.service.js` to use central config
- Update `server/middleware/auth.middleware.js` to 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 `authenticateToken` middleware to all routes in `server/routes/documents.js`
- Remove all `req.user?.id || 'test-user-id'` patterns
- Use `req.user.userId` directly (set by middleware)
- Add `requireOrganizationMember` where organization scoping needed
- Test: Verify 401 on unauthenticated requests
**Agent 3: Search/Upload/Image Route Auth**
- Secure `server/routes/search.js` (both `/token` and `/search`)
- Secure `server/routes/upload.js` and `/upload/quick-ocr`
- Secure `server/routes/images.js` (all image endpoints)
- Remove `test-user-id` fallbacks
- Add organization membership checks
**Agent 4: Stats & Jobs Auth**
- Secure `server/routes/stats.js` with `requireSystemAdmin` for global stats
- Create `/stats/organizations/:organizationId` for scoped stats
- Secure `server/routes/jobs.js` with proper auth
- Remove public access to operational metrics
**Agent 5: Meilisearch Token Hardening**
- Modify `server/routes/search.js` POST `/token` to 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.js` with `parsePagination(req, options)`
- Support `?page=1&pageSize=50` query 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.js` GET `/` with pagination
- Update `server/routes/images.js` listings
- Update `server/routes/jobs.js` GET `/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.js` using 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 = 404` for 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_organizations` to 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.js` to import marine.css
- Test: Verify all interactive elements meet 60px minimum
**Agent 14: ARIA Labels & Alt Text**
- Scan all `.vue` files for icon-only buttons
- Add `aria-label` to all interactive elements without visible text
- Add `aria-hidden="true"` to decorative icons inside labeled buttons
- Add `:alt` attributes 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-metric` class for critical numbers (32-48px, font-weight: 700)
- Apply to key metrics in dashboard/stats components
- Add `.nd-heading` class (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
```javascript
// 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;
```
```javascript
// 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
```javascript
// 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
```javascript
// 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
```javascript
// 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
```javascript
// 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
```javascript
// 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
```javascript
// 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
```css
/* 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
```vue
<!-- BEFORE -->
<button @click="deleteItem">
<TrashIcon />
</button>
<!-- AFTER -->
<button
type="button"
aria-label="Delete item"
@click="deleteItem"
>
<TrashIcon aria-hidden="true" />
</button>
```
```vue
<!-- 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
```bash
git clone https://github.com/dannystocker/navidocs.git
cd navidocs
git checkout navidocs-cloud-coordination
```
### Step 2: Analyze Current State
```bash
# 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:
1. Read relevant files from GitHub
2. Implement their assigned fixes
3. Test changes locally
4. Commit with descriptive message
5. Report completion status
### Step 4: Integration & Testing
After all 15 agents complete:
1. **Build verification:**
```bash
npm install
npm run build
du -sh dist/ # Verify <500KB gzipped target
```
2. **Security audit:**
```bash
npm audit --production
grep -r "test-user-id" server/ # Should be 0 results
grep -r "JWT_SECRET.*||" server/ # Should be 0 results
```
3. **UX verification:**
```bash
# 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
```
4. **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
### Step 5: Commit & Push
```bash
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-id` now 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:
```bash
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-coordination` branch only
- Tag completion: `v1.0.0-security-ux-fixes`
---
**Ready to execute. Spawn 15 Haiku agents in parallel and implement all critical fixes.**