- useTimelineDrag: onProjectBarMouseDown and single-alloc drag path now reset
multiSelectRef + multiSelectState before starting a new drag, so the
FloatingActionBar is dismissed immediately when an unrelated drag begins
- FloatingActionBar.test.tsx: 4 regression tests for the null-render guard
(count=0) and all three label variants
- useTimelineSSE.test.ts: 2 new tests — tab hides during pending reconnect
timer (clears timer, resyncs on next open) and first-ever connection fails
before any open (retry open still resyncs correctly)
- assistant-tools-user-admin-inventory-read.test.ts: add isActive to expected
findMany select shape (already in production, test was stale)
Co-Authored-By: claude-flow <ruv@ruv.net>
- Invite flow: admin can invite users by email with role selection; accept-invite page
sets password and creates the account; 72-hour token expiry; E2E tests
- User deactivate/reactivate/delete: new tRPC procedures + UI buttons; deactivation
revokes all active sessions immediately; delete cascades vacation/broadcast records;
isActive field added via migration 20260402000000_user_isactive
- Auth: block login for inactive users with audit entry
- Favicon: SVG favicon + ICO/PNG fallbacks (16, 32, 180, 192, 512px); manifest updated
- Dashboard: GridLayout dynamic-import loading skeleton prevents blank dark area
on first login before react-grid-layout chunk is cached
- Admin users: remove max-w-5xl constraint so table uses full page width
- Dev: docker container restart workflow documented in LEARNINGS.md; Prisma generate
must run inside the container after schema changes (named node_modules volume)
Co-Authored-By: claude-flow <ruv@ruv.net>
#41 (critical): Replace plain Error throws in authorize() with CredentialsSignin
subclasses (MfaRequiredError / MfaRequiredSetupError / InvalidTotpError).
Auth.js v5 forwards CredentialsSignin.code to the client via SignInResponse.code;
plain throws become CallbackRouteError and the message is never visible.
Signin page now checks result.code ?? result.error for exact code matching.
#38: MfaPromptBanner converted to fully client-side component via
trpc.user.getMfaStatus.useQuery() — disappears immediately after MFA enable
without requiring page reload. Snooze key remains userId-scoped via useSession().
Server-side prisma.user.findUnique call removed from (app)/layout.tsx.
#40: NEXTAUTH_URL default fallback removed from docker-compose.yml.
The variable is now required (:?) — docker compose up fails with a descriptive
error if the value is missing, preventing silent localhost redirect bugs.
Tests: auth.test.ts (5), MfaPromptBanner.test.ts (7), reset-password.test.ts (6)
All new tests green. pnpm --filter @capakraken/web exec tsc --noEmit clean.
Co-Authored-By: claude-flow <ruv@ruv.net>
#28 - TOTP rate limiting (verifyTotp): added totpRateLimiter (10 req/30s),
throws TOO_MANY_REQUESTS before DB hit; 16 unit tests including rate-limit
exceeded + userId key isolation.
#29 - /api/reports/allocations role check: only ADMIN/MANAGER/CONTROLLER may
access; returns 403 otherwise; 9 unit tests (401 unauthenticated, 403 for
USER/VIEWER, 200 for allowed roles + xlsx format).
#31 - pgAdmin credentials moved out of docker-compose.yml into env vars;
PGADMIN_PASSWORD is now required (:?) to prevent accidental plaintext
exposure in committed files.
#34 - Server-side HTML sanitization for comment bodies via stripHtml():
strips all tags + decodes safe entities before persistence; 16 unit tests
covering passthrough, injection patterns, entity decoding.
#35 - MFA setup prompt banner (MfaPromptBanner): shown to ADMIN/MANAGER users
without TOTP enabled; user-scoped localStorage snooze (7 days); links to
/account/security; accessibility role=alert; 7 structural unit tests.
#33 - Auth anomaly alerting cron (/api/cron/auth-anomaly-check): detects
HIGH_GLOBAL_FAILURE_RATE and CONCENTRATED_FAILURES in 30-minute window;
CRITICAL notification to ADMINs; fail-closed via verifyCronSecret;
10 unit tests.
#32 - MFA enforcement policy: added requireMfaForRoles field to SystemSettings
schema + Prisma migration; auth.ts blocks login with MFA_REQUIRED_SETUP
signal if role is enforced but TOTP not set up; signin page redirects to
/account/security?mfa_required=1; settings schema + view model updated;
11 unit tests.
#30 - API keys architecture decision documented in LEARNINGS.md; no code
written — product decision required before implementation.
Co-Authored-By: claude-flow <ruv@ruv.net>
New users on a shared device were picking up a previous user's stale
(potentially empty) dashboard layout from localStorage because the key
"capakraken_dashboard_v1" was not user-scoped.
- useDashboardLayout: key is now capakraken_dashboard_v1_{userId};
userId is resolved via trpc.user.me before touching localStorage
- Initial state falls back to createDefaultDashboardLayout() until
userId resolves, then hydrates from the user-scoped key
- DB layout still wins over localStorage when it has data (unchanged)
- E2E test suite covers: new-user flow, modal widget list, add widget
persists after reload, cross-user localStorage isolation
- plan.md: added ticket #27 implementation plan
Co-Authored-By: claude-flow <ruv@ruv.net>
#19 MFA QR code: render locally via qrcode package, remove external qrserver.com request
#20 Webhook SSRF: add ssrf-guard.ts with DNS-verified IP blocklist; enforce on create/update/test/dispatch
#21 /api/perf: fail-closed when CRON_SECRET missing; remove query-string token auth
#22 CSP: remove unsafe-eval and unsafe-inline from script-src in production builds
#23 Active session registry: forward jti into session object; validate against ActiveSession on every tRPC request
#24 Docker: add missing packages/application to Dockerfile.dev; fix pnpm-lock.yaml glob;
run db:migrate:deploy on container start so a fresh checkout boots without manual steps
Also: fix pre-existing TS error in e2e/allocations.spec.ts (args.length literal type overlap)
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>
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>
- Add DALL-E cover art generation for projects (Azure OpenAI + standard OpenAI)
- CoverArtSection component with generate/upload/remove/focus-point controls
- Client-side image compression (10MB input → WebP/JPEG, max 1920px)
- DALL-E settings in admin panel (deployment, endpoint, API key)
- MCP assistant tools for cover art (generate_project_cover, remove_project_cover)
- Rename "Planarchy" → "plANARCHY" across all UI-facing text (13 files)
- Fix hardcoded canEdit={true} on project detail page — now checks user role
- Computation graph visualization (2D/3D) for calculation rules
- OG image and OpenGraph metadata
Co-Authored-By: claude-flow <ruv@ruv.net>
AI Assistant (HartBOT):
- Chat panel with inline layout, session persistence, message history (up-arrow recall)
- OpenAI function calling with 20+ tools (search, navigate, create/cancel allocations, update status)
- RBAC-aware tool filtering, fuzzy search with word-level matching
- Navigation actions (router.push) and data invalidation after mutations
- Country/metro city/org unit/role filtering on resource search
Demand Filling Enhancements:
- Two-phase fill modal: plan multiple resources, then confirm & assign all at once
- Availability preview per resource (available/partial/conflict days, existing bookings)
- Coverage bar showing demand hours distribution across assigned resources
- Fill demand from project detail page (new Assign button per demand)
- Fixed: filled demands no longer shown on timeline, demand bars no longer overlap
Budget per Role:
- DemandRequirement.budgetCents field (schema + API + UI)
- Project wizard step 3: budget input per role with allocation summary bar
- Project detail: allocated vs booked budget per demand
- Fill demand modal: role budget display with cost estimates
- AllocationModal: budget field for demand editing
Project Favorites:
- User.favoriteProjectIds (JSONB) with toggle API
- Star button on projects list and detail page (optimistic updates)
- "My Projects" dashboard widget (favorites + responsible person projects)
Project Management:
- Edit project from detail page (ProjectModal integration)
- Edit demands from detail page (AllocationModal integration)
- Admin-only project deletion (cascades assignments + demands)
- Create user accounts from admin panel
Timeline Fixes:
- Country multi-select filter with backend support
- URL param sync for same-page navigation (AI assistant integration)
- Demand lane stacking (no more overlapping bars)
- Single-day booking resize handles (always visible, min 6px)
- Single-day resize allowed (start === end)
- "All Clients" toggle (select all / deselect all)
Other Fixes:
- crypto.randomUUID fallback for non-secure contexts
- Chat message limit raised (200 max, client sends last 40)
- Status dropdown portal (no longer clipped by table overflow)
- Cents display restored in budget views (2 decimal places)
- Allocations grouped view with project sub-groups (collapsed by default)
- Server-side resource search for project wizard (no 500 limit)
Co-Authored-By: claude-flow <ruv@ruv.net>