Mapped all Accenture IS Standards against CapaKraken tech stack.
19 standards relevant, ~68 not applicable.
Key findings:
- Application Security Standard: 73% compliant (already analyzed)
- Gen AI + Agentic AI Standards: NEW, critical for HartBOT — must read
- PostgreSQL, nginx, Container, DevSecOps: need gap analysis
- 12 action items across 4 priority tiers
Co-Authored-By: claude-flow <ruv@ruv.net>
42 OK (67%), 9 PARTIAL (14%), 8 TODO (13%), 4 N/A (6%)
Full mapping of all EAPPS controls across 20 categories.
Co-Authored-By: claude-flow <ruv@ruv.net>
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>
Project names in the health widget now link to /projects/[id] detail page.
Hover: brand color transition for visual feedback.
Co-Authored-By: claude-flow <ruv@ruv.net>
The static import of @sentry/nextjs at module level triggered worker
thread creation even when withSentryConfig was only called in production.
This caused recurring "Cannot find module vendor-chunks/lib/worker.js"
crashes that killed the dev server mid-request.
Fix: replaced static import with dynamic require() inside a
NODE_ENV === "production" block. In dev mode, the Sentry module
is never loaded at all.
Co-Authored-By: claude-flow <ruv@ruv.net>
Widget: added "Shoring" column with ShoringBadge per project showing
offshore % with color indicator (green/yellow/red).
Backend: added id field to ProjectHealthRow for badge queries.
Database: assigned diverse countries to 11 resources for realistic
shoring data (25 DE, 5 ES, 4 IN, 2 US instead of all-DE).
Co-Authored-By: claude-flow <ruv@ruv.net>
The Project View used its own buildProjectRowGridBackground() which
rendered CSS gradients with hardcoded rgba colors (no dark mode).
The Resource View used shared gridLines from useTimelineLayout which
renders React div elements with proper dark: Tailwind classes.
Fix: replaced the CSS gradient approach with the shared gridLines
in both resource rows and open demand rows within the Project View.
Removed the now-unused buildProjectRowGridBackground function (~40 LOC).
Both views now use identical grid lines with:
- Brand-colored today marker
- Amber weekend highlights
- Proper dark mode colors via Tailwind classes
Co-Authored-By: claude-flow <ruv@ruv.net>
resource.list returns { resources: [...], total } but ScenarioPlanner
expected a flat array. Fixed by extracting .resources from the response.
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 withSentryConfig() wrapper caused recurring worker.js crashes
in Next.js dev mode (vendor-chunks/lib/worker.js MODULE_NOT_FOUND).
This crashed the server mid-request during image generation and
other long-running operations.
Fix: only apply withSentryConfig in production. In dev mode, use
the raw Next.js config. Sentry instrumentation also gated to
production only.
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>
Both import pages are now grouped under the ACN-Orga collapsible
section in the admin sidebar instead of being separate top-level items.
ACN-Orga submenu now contains:
- Countries
- Org Units
- Util. Categories
- Mgmt Levels
- Dispo Import (moved)
- Skill Import (moved)
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>
React Rules of Hooks violation: filteredTop and filteredWatch useMemo
hooks were placed AFTER the isLoading early return, causing "Rendered
more hooks than during the previous render" error that crashed the
entire dashboard.
Fix: moved rawTop/rawWatch/month declarations and filteredTop/filteredWatch
useMemo hooks BEFORE the isLoading early return so hooks always run
in the same order.
Verified in Chrome: dashboard loads with zero console errors.
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>
- Zod schema: responsiblePerson now min(1) required, no longer optional
- ProjectModal: required indicator (*), HTML required attribute, no undefined fallback
- ProjectWizard: same fix for create flow
- Existing projects with null responsiblePerson still work (DB allows null)
- Validation enforced at API boundary on new creates/updates
Co-Authored-By: claude-flow <ruv@ruv.net>
The tooltip shown when hovering over project strips in the timeline
now includes additional information:
- Role name (e.g. "3D Artist", "Project Manager")
- Assignment date range (2026-03-01 → 2026-06-30)
- Status badge when not CONFIRMED (shows PROPOSED, DRAFT, etc.)
- Lead person and order type on the same line
Data comes from already-loaded timeline entries — no extra API calls.
Safe change: tooltip is pointer-events-none and read-only.
Co-Authored-By: claude-flow <ruv@ruv.net>
The useInfiniteQuery with tRPC as-any cast wasn't passing cursors
correctly, causing no data to load on initial page visit.
Replaced with simple useQuery + manual cursor state:
- First 50 entries load immediately on mount
- "Load more" button appends next page via cursor
- Filter changes reset cursor and entries
- keepPreviousData prevents flash during pagination
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>
The sub-text (e.g. "36 active", "30 total") was rendering inline
next to the large number, creating confusing "36₃₆ active" appearance.
Fixed by wrapping the number in a block <div> and the sub-text in a
block <p>, ensuring they stack vertically.
Co-Authored-By: claude-flow <ruv@ruv.net>
Budget Forecast Widget:
- Project: "Active projects with a defined budget"
- Budget Usage: "Percentage of total budget consumed by current allocations"
- Burn/mo: "Monthly burn rate based on currently active allocations"
- Exhaustion: "Projected date when budget will be fully consumed"
Project Health Widget:
- Project: "Active projects scored across three health dimensions"
- B / S / T: "Budget health, Staffing health, Timeline health"
- Score: "Composite score: average of Budget, Staffing, Timeline (0-100)"
- Score badge: dark theme variants for green/amber/red
Skill Gap Widget:
- Skill: "Skills required by open demand positions"
- Demand: "Number of unfilled demand requirements needing this skill"
- Supply: "Number of active resources with this skill at proficiency 3+"
- Gap: "Supply minus Demand: negative = shortage, positive = surplus"
All three: added dark:bg-gray-800/50 thead, dark:divide-gray-800 tbody,
dark:hover:bg-gray-800/30 rows, dark:text-gray-100/300/400 text colors.
Co-Authored-By: claude-flow <ruv@ruv.net>
WidgetContainer redesign:
- Remove grey bg-gray-50/50 header background (clashed with dark theme)
- Header now uses transparent background with subtle separator line
- Add description prop — shows catalog description under title
- Drag grip dots appear on hover (6-dot pattern, hidden by default)
- Remove button appears on hover with red highlight
- Dark theme: bg-gray-900, border-gray-700/60, gray-800 separator
- Drag state: ring-2 ring-brand-400/30 with scale effect
- Hover state: shadow-md with border color transition
DashboardClient: passes widget.description to WidgetContainer
Co-Authored-By: claude-flow <ruv@ruv.net>
Combines SkillsAnalytics (496 LOC) and SkillMarketplace (346 LOC) into
a single tabbed Skills Hub (770 LOC total, -9% code).
New structure:
- skills/shared.tsx: ProficiencyBadge, GapIndicator, constants (extracted)
- skills/OverviewTab.tsx: KPI cards, top 10 table, distribution chart, export
- skills/SearchTab.tsx: skill search + proficiency + availability filter
- skills/GapsTab.tsx: supply vs demand table with gap indicators
- skills/PeopleFinderTab.tsx: multi-rule AND/OR builder, chapter filter, export
- SkillsHub.tsx: tabbed container with URL-persisted tab state (?tab=)
Routing:
- /analytics/skills renders SkillsHub (was SkillsAnalytics)
- /analytics/skill-marketplace redirects to /analytics/skills?tab=search
- Sidebar: "Skill Marketplace" removed, renamed to "Skills Hub"
No API changes — reuses existing queries with conditional fetching per tab.
Full dark theme support on all components.
Co-Authored-By: claude-flow <ruv@ruv.net>
- Tree structure: clients rendered as indented tree with visual connectors
- Expand/collapse: chevron toggle, children count badge, expand/collapse all
- Parent selector: dropdown on add + "Move..." inline picker on each client
(excludes self and descendants to prevent circular refs)
- Search: shows matching clients AND their ancestors for context
- Depth visualization: alternating backgrounds, border-l connectors
- Drag-and-drop: preserved, reorders among siblings only
No API changes needed — router already supported parentId in CRUD.
Co-Authored-By: claude-flow <ruv@ruv.net>