The shoring indicator logic was backwards. In the business context,
higher offshore = more cost-efficient = GOOD.
Inverted logic:
- Green: offshore >= threshold (target met, e.g. >= 55%)
- Yellow: offshore close to threshold (threshold-10 to threshold)
- Red: offshore below threshold (too little offshore, too expensive)
Updated:
- ShoringIndicator: getSeverity() inverted, badge text updated
- ProjectModal: "Max Offshore" renamed to "Min Offshore" with new tooltip
- AI Tool: status text reflects "target met" vs "below target"
- Tool description: "higher offshore is better, threshold is minimum"
Co-Authored-By: claude-flow <ruv@ruv.net>
When generating multiple cover images in one conversation, the
accumulated tool results (each ~400KB base64) caused the OpenAI
conversation payload to exceed JSON parsing limits in the browser.
Fix:
- Strip coverImageUrl from invalidate action results (not needed by AI)
- Cap invalidate results to 4KB
- Cap all tool results to 8KB
- Prevents "JSON.parse: unexpected character" errors during batch ops
Co-Authored-By: claude-flow <ruv@ruv.net>
The tool was hardcoded to only check isDalleConfigured(), ignoring
Gemini even when it was the configured image provider. This caused
"DALL-E is not configured" errors for all 13 projects.
Fix: reads imageProvider from SystemSettings and routes to Gemini
or DALL-E accordingly (same logic as the generateCover mutation).
Co-Authored-By: claude-flow <ruv@ruv.net>
Corrected model names (per Google AI docs):
- gemini-2.5-flash-image (was gemini-2.0-flash-preview-image-generation)
- gemini-3-pro-image-preview (Nano Banana Pro)
- gemini-3.1-flash-image-preview (Nano Banana 2)
UI: replaced text input with dropdown selector showing all 3 models
with human-readable descriptions.
Default changed to gemini-2.5-flash-image (fast, high-volume).
AI Assistant: generate_project_cover tool description updated to be
provider-agnostic (works with both DALL-E and Gemini).
Co-Authored-By: claude-flow <ruv@ruv.net>
API: new testGeminiConnection adminProcedure
- Generates a simple test image via Gemini API
- Returns { ok, model } on success, { ok: false, error } on failure
- Audit logged: "Gemini test succeeded/failed"
UI: "Test Gemini" button next to "Save Image Settings"
- Only visible when Gemini provider is selected
- Shows green success or red error result below the buttons
- Displays the model name on success
Model: gemini-2.0-flash-preview-image-generation (correct name)
Co-Authored-By: claude-flow <ruv@ruv.net>
Schema:
- SystemSettings: geminiApiKey, geminiModel, imageProvider fields
- imageProvider: "dalle" (default) or "gemini"
Gemini Client (packages/api/src/gemini-client.ts):
- Direct HTTP call to Gemini REST API with responseModalities: [TEXT, IMAGE]
- Returns base64 data URL
- Error parsing with user-friendly messages
Router (project.ts):
- generateCover: routes to DALL-E or Gemini based on imageProvider setting
- New isImageGenConfigured query returning { configured, provider }
Admin UI (SystemSettingsClient.tsx):
- "Image Generation" section with provider radio buttons (DALL-E / Gemini)
- Conditional fields: DALL-E config or Gemini API key + model
- Separate save button for image settings
Security:
- geminiApiKey sanitized in audit logs (SENSITIVE_FIELDS)
- API key stored server-side only, never sent to client
Co-Authored-By: claude-flow <ruv@ruv.net>
API: new updateName adminProcedure in user router
- Input: userId + name (min 1, max 200 chars)
- Argon2 not involved (name only, not password)
- Audit log: "Changed name from X to Y"
UI: "Display Name" editable section in user edit modal
- Shows current name with "Edit" link
- Click Edit: inline input with Save/Cancel + Enter/Escape
- Auto-focuses input, saves on Enter
- Invalidates user list on success
Co-Authored-By: claude-flow <ruv@ruv.net>
Admin Set Password:
- New setPassword adminProcedure in user router (Argon2 hashing)
- Audit log: "Password reset by admin" (no password value logged)
- UI: per-user "Password" button with key icon in User Management
- Modal: new password + confirm, min 8 chars, mismatch validation
- Success toast + auto-close on completion
Dashboard fix:
- Corrupted .next cache causing "Cannot find module worker.js"
- Fixed by clearing .next cache and restarting dev server
Co-Authored-By: claude-flow <ruv@ruv.net>
Engine (packages/engine):
- New checkDuplicateAssignment() pure function: detects same resource
assigned to same project with overlapping dates
- 15 unit tests covering: overlap, no-overlap, cancelled, self-exclude,
string dates, PROPOSED status
Application layer (packages/application):
- createAssignment: throws CONFLICT before DB write if duplicate found
- fillDemandRequirement: same check before entering transaction
AI Assistant (packages/api/router/assistant-tools.ts):
- create_allocation: checks before creating, returns helpful error message
- fill_demand: same check using demand's projectId
UI (apps/web):
- AllocationModal: amber warning when resource already assigned to
selected project with overlapping dates (non-blocking)
Database cleanup:
- Found and merged 1 duplicate: Wong Wong on Porsche Taycan Sport Film
(2 overlapping PROPOSED assignments merged into 1)
Regression: 298 engine tests pass (283 + 15 new). TypeScript clean.
Co-Authored-By: claude-flow <ruv@ruv.net>
- List query: exclude changes JSONB from select (only metadata)
- Default to last 30 days when no date filter (avoids full table scan)
- New getById query: fetches full changes JSONB on demand
- ExpandedDiff component: fetches diff only when user expands an entry
- 5-minute staleTime on expanded diffs (cacheable, rarely changes)
Co-Authored-By: claude-flow <ruv@ruv.net>
Schema:
- Client model: add tags String[] field
- Shared types + Zod schemas updated for tags
API:
- client.create/update: accept tags array
- client.delete: with safety checks (no projects, no children)
- client.batchUpdateSortOrder: batch reorder in transaction
UI (complete redesign of ClientsAdminClient):
- Drag-and-drop reordering via @dnd-kit (sortable)
- Inline editing: click name/sortOrder to edit in-place
- Tag pills: auto-colored by hash, add/remove inline
- Tag auto-suggest from existing tags across all clients
- Sticky "Add Client" input row at top
- Search/filter by name, code, or tag
- Delete with inline confirmation
- Optimistic reorder (instant UI update)
- Full dark theme support
Co-Authored-By: claude-flow <ruv@ruv.net>