- Install husky v9 + lint-staged: pre-commit runs eslint --fix and prettier on staged files
- Tighten ESLint base config: no-console→error, ban-ts-comment (ts-ignore banned, ts-expect-error with description allowed), reportUnusedDisableDirectives→error
- Migrate web app from deprecated `next lint` to `eslint src/` with flat config and react-hooks plugin
- Convert all 5 @ts-ignore to @ts-expect-error with descriptions, remove stale disable comments
- Add NEXT_PUBLIC_SENTRY_DSN to docker-compose.prod.yml and .env.example
- Add coverage artifact upload step to CI test job
- Pre-existing violations (102 warnings) downgraded to warn in web config for Phase 2 cleanup
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Password validation: min(8) → min(12) across auth.ts, user-procedure-support.ts,
and invite.ts (aligns with NIST SP 800-63B modern recommendations)
- Error boundary: stop rendering raw error.message which could leak internal
details; always show the generic fallback text
- Add `pnpm audit` script (--audit-level=high) for dependency vulnerability scanning
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace max-w-5xl/max-w-7xl constrained wrappers with the app-page utility
class, consistent with other full-width pages like the projects list.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Runtime errors in components now show a friendly "Something went wrong"
screen instead of a white page. Timeline and staffing panel are
individually wrapped. Route-level error.tsx handles server component errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Resources, projects, and allocations filter state now syncs to/from
URL so filters survive refresh and can be shared via link.
Text inputs are debounced (300ms) to avoid URL churn.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Read-only capacity snapshot with utilization donut, top 5 active projects,
open demand alert banner, and quick-link grid — single-column card layout
optimised for PWA standalone mode.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Shows resources with available capacity in a selected date window.
- Filter by date range (with DateRangePresets), min hours/day slider, and free-text search
- Cards show role, chapter, available h/day with color-coded capacity bar
- Links to individual resource profiles
- "Bench" nav entry added to Resources section in AppShell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- EmptyState shared component; replace AllocationsClient inline empty state
- DateRangePresets (this month/quarter/3 months/year) integrated into AllocationModal
- Debounce conflict-check inputs in AllocationModal (400ms) using existing useDebounce
- Dashboard layout save feedback via SuccessToast after DB write completes
- Scenarios nav item in Planning sidebar + /scenarios list page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add batchHardDelete adminProcedure to resource-mutations router
- Per-row Delete button visible to ADMIN role only
- Delete Selected button in BatchActionBar for ADMIN role only
- Two-step confirmation dialogs with permanent-action warnings
- Audit log written for each deleted resource
Co-Authored-By: claude-flow <ruv@ruv.net>
#58: Split the merged "Type: BD / INT" field in the wizard review step into
separate "Order Type", "Allocation Type", and "Status on create" rows so
users can clearly distinguish commercial classification from lifecycle status.
#60: Relabel FillOpenDemandModal staging CTA from "+ Add to Plan" to
"+ Queue Assignment" and the proceed CTA from "Review (N)" to
"Review Queued (N)" to make the staged/non-final nature of the action clear.
Also correct the project detail Assignments label from "N active" to
"N planned" and update the tooltip to include PROPOSED in the definition.
Co-Authored-By: claude-flow <ruv@ruv.net>
#66: Project detail "Open Demands" summary incorrectly counted COMPLETED
demands as open. Fix: add `status !== "COMPLETED"` to the activeDemands
filter in /projects/[id]/page.tsx.
#59/#67: Project creation and edit had two bugs:
1. Both invalidated `project.list` but the page queries `project.listWithCosts`
— the list never refreshed after a save.
2. Success toasts were either absent (ProjectModal) or mounted inside the
wizard component that unmounts before the toast finishes.
Fix: correct invalidation key to listWithCosts; add optional onSuccess prop
to both ProjectWizard and ProjectModal; ProjectsClient wires onSuccess to a
persistent SuccessToast rendered outside the modals.
Co-Authored-By: claude-flow <ruv@ruv.net>
#55: Add SuccessToast after new resource is created. ResourceModal gains an
optional onSuccess(displayName) prop; ResourcesClient wires it to a toast
that auto-dismisses after 2.5 s.
#56: Fix useFocusTrap stale-closure bug. Focusable elements are now queried
dynamically inside handleKeyDown (not captured once at mount), so Tab
navigation stays correct as the form re-renders. Initial focus is deferred
via requestAnimationFrame so the browser layout is stable before focus() fires.
Co-Authored-By: claude-flow <ruv@ruv.net>
- Split auth config into auth.config.ts (edge-safe, no argon2) and auth-edge.ts
for middleware use; auth.ts now spreads the shared config
- Middleware wraps with auth() to redirect unauthenticated requests to /auth/signin
before any page render; passes through /auth/, /api/, /invite/ paths
- SessionGuard client component watches useSession() and redirects on
status=unauthenticated, closing the SPA navigation gap
- QueryCache + MutationCache in TRPCProvider redirect on UNAUTHORIZED tRPC errors
without retrying; SessionProvider polls session state every 5 minutes
- Middleware tests updated for async auth wrapper and auth-edge mock
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>
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>
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>
Remove overflow-hidden from timeline page wrapper — it was clipping
the ProjectCombobox and ResourceCombobox dropdown menus. The
max-h-[100dvh] constraint combined with min-h-0 on flex children
is sufficient to keep the scroll container within viewport bounds.
Co-Authored-By: claude-flow <ruv@ruv.net>
Staffing "Assign" Button:
- Inline assignment form on each suggestion card in StaffingPanel
- Pre-fills project, dates, hours from search criteria
- 1-click confirm creates allocation with PROPOSED status
- Success/error toasts, removes assigned suggestions from list
Dashboard Redis Caching:
- New cache utility (packages/api/src/lib/cache.ts) with get/set/invalidate
- All 5 dashboard queries wrapped with 60s TTL cache-aside pattern
- Auto-invalidation on allocation + project mutations (fire-and-forget)
- Graceful fallthrough to DB if Redis unavailable
Bulk Operations:
- CSV export for selected resources and projects (apps/web/src/lib/csv-export.ts)
- Project batch delete mutation with cascade (assignments, demands, rules)
- Export/Delete buttons added to BatchActionBar on both list pages
Budget Overrun Notifications:
- checkBudgetThresholds() alerts at 80% (HIGH) and 100% (URGENT)
- Called after every allocation mutation, duplicate-safe
- Targets ADMIN + MANAGER users with SSE delivery
Estimate Approval Reminders:
- checkPendingEstimateReminders() finds SUBMITTED versions > 3 days old
- Cron endpoint: GET /api/cron/estimate-reminders (optional CRON_SECRET auth)
- Creates in-app REMINDER notifications, duplicate-safe
Co-Authored-By: claude-flow <ruv@ruv.net>
- ResourceHoverCard: add isInitialLoading to useEffect deps so
mouseover/mouseout listeners attach after canvas mounts
- PreferencesModal: lift prefsOpen state to AppShell, render modal
outside sidebar's backdrop-blur stacking context
- Timeline page: constrain to max-h-[100dvh] overflow-hidden so
horizontal scrollbar is accessible without scrolling to bottom
- Multi-drag: pass selectedAllocationIds from ref at drag completion
to prevent stale closure in onMultiDragComplete callback
Co-Authored-By: claude-flow <ruv@ruv.net>
- Extract shared render helpers (vacation blocks, range overlay, overbooking blink) into renderHelpers.tsx
- Centralize status badge styles and vacation color maps into status-styles.ts
- Extract dragMath.ts utility from useTimelineDrag for reuse
- Split useInvalidatePlanningViews into useInvalidateTimeline (4 queries) + useInvalidatePlanningViews (8 queries)
- Adopt findUniqueOrThrow() and Prisma select constants across API routers
- Add shared fmtEur() helper for API-side money formatting
- Wrap TimelineResourcePanel and TimelineProjectPanel with React.memo
- Fix pre-existing TS2589 deep type errors in TeamCalendar and VacationModal
- 38 files changed, reducing ~400 lines of duplicated code
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>