---
phase: 01-foundation
plan: 04
subsystem: ui
tags: [nextjs, next-auth, tanstack-query, react-hook-form, zod, shadcn, fastapi, bcrypt, tailwind]

# Dependency graph
requires:
  - phase: 01-foundation plan 01
    provides: "PostgreSQL schema with Tenant, Agent, PortalUser ORM models and RLS infrastructure"
provides:
  - FastAPI portal API router at /api/portal (tenant CRUD, agent CRUD, auth verify/register)
  - Next.js 16 admin portal at localhost:3000 with login, tenant management, and Agent Designer
  - Auth.js v5 Credentials provider validating against FastAPI /auth/verify endpoint
  - proxy.ts (Next.js 16 auth proxy) protecting all routes except /login and /api/auth/*
  - Integration tests for all portal API CRUD operations (38 tests)
affects: [02-channel-expansion, 03-polish-launch, operator-workflow]

# Tech tracking
tech-stack:
  added:
    - "bcrypt>=4.0.0 (password hashing in shared package)"
    - "next-auth@5.0.0-beta.30 (Auth.js v5 with Credentials provider)"
    - "@tanstack/react-query@^5 (server state and cache management)"
    - "react-hook-form@^7 (form management)"
    - "zod@^4 (schema validation)"
    - "@hookform/resolvers@^5 (standard-schema resolver for zod v4 compat)"
    - "shadcn/ui (button, card, input, textarea, select, badge, table, dialog, separator, label)"
    - "lucide-react (icons)"
  patterns:
    - "FastAPI APIRouter with prefix /api/portal for portal-specific endpoints"
    - "Portal API sets RLS current_tenant_id context for agent endpoints (admin bypasses but still scopes correctly)"
    - "Auth.js v5 JWT strategy — no DB session table, stateless tokens"
    - "Next.js 16 route groups: (auth) for login, (dashboard) for protected routes with shared layout"
    - "TanStack Query hooks in lib/queries.ts with standardized query key hierarchy"
    - "standardSchemaResolver from @hookform/resolvers/standard-schema for zod v4 + hookform v5 compat"
    - "proxy.ts instead of middleware.ts — Next.js 16 renamed middleware to proxy"

key-files:
  created:
    - "packages/shared/shared/api/__init__.py — portal_router export"
    - "packages/shared/shared/api/portal.py — FastAPI portal router with all CRUD endpoints"
    - "tests/integration/test_portal_tenants.py — 23 tenant CRUD integration tests"
    - "tests/integration/test_portal_agents.py — 15 agent CRUD + Agent Designer field tests"
    - "packages/portal/proxy.ts — auth proxy protecting dashboard routes"
    - "packages/portal/lib/auth.ts — Auth.js v5 config with Credentials provider"
    - "packages/portal/lib/api.ts — typed fetch wrapper + API type definitions"
    - "packages/portal/lib/queries.ts — TanStack Query hooks for tenants and agents"
    - "packages/portal/components/agent-designer.tsx — Agent Designer form (Identity/Personality/Config/Capabilities/Escalation/Status)"
    - "packages/portal/components/tenant-form.tsx — reusable tenant create/edit form"
    - "packages/portal/components/nav.tsx — sidebar navigation (Dashboard/Tenants/Employees)"
    - "packages/portal/app/(dashboard)/layout.tsx — shared dashboard layout with QueryClientProvider"
    - "packages/portal/app/(dashboard)/dashboard/page.tsx — dashboard landing with tenant count"
    - "packages/portal/app/(dashboard)/tenants/page.tsx — tenant list with table UI"
    - "packages/portal/app/(dashboard)/tenants/new/page.tsx — tenant creation"
    - "packages/portal/app/(dashboard)/tenants/[id]/page.tsx — tenant detail/edit/delete"
    - "packages/portal/app/(dashboard)/agents/page.tsx — AI employees card grid"
    - "packages/portal/app/(dashboard)/agents/new/page.tsx — Agent Designer create"
    - "packages/portal/app/(dashboard)/agents/[id]/page.tsx — Agent Designer edit"
    - "packages/portal/app/(auth)/login/page.tsx — email/password login form"
    - "packages/portal/app/api/auth/[...nextauth]/route.ts — Auth.js route handler"
  modified:
    - "packages/shared/pyproject.toml — added bcrypt>=4.0.0 dependency"
    - "docker-compose.yml — added portal service on port 3000"
    - "packages/portal/app/layout.tsx — updated metadata title to 'Konstruct Portal'"
    - "packages/portal/app/page.tsx — redirect to /dashboard or /login based on auth state"

key-decisions:
  - "proxy.ts used instead of middleware.ts — Next.js 16 renamed middleware to proxy (deprecated warning)"
  - "standardSchemaResolver from @hookform/resolvers/standard-schema used instead of zodResolver — @hookform/resolvers v5 removed dedicated zod subpackage in favor of Standard Schema protocol; zod v4 implements Standard Schema"
  - "Route group (dashboard) used to share QueryClientProvider layout across /dashboard, /tenants, /agents routes without nesting them under a path prefix"
  - "Portal agent endpoints set RLS current_tenant_id context before DB operations — platform admin API still operates within tenant scope per RLS policy"
  - "Auth.js v5 JWT session strategy — no portal_sessions table needed for Phase 1; stateless tokens sufficient until enterprise SSO requirements"
  - "Agent Designer uses employee-centric labels: Employee Name, Job Title, Job Description (persona), Statement of Work (system_prompt)"

patterns-established:
  - "Pattern 1: FastAPI portal router sets RLS tenant context via current_tenant_id ContextVar for all agent CRUD operations"
  - "Pattern 2: Integration tests use make_app(session) factory to override get_session dependency with test DB session"
  - "Pattern 3: Next.js 16 uses proxy.ts for edge-layer auth protection; Auth.js v5 auth() called directly in proxy"
  - "Pattern 4: TanStack Query key hierarchy [resource, ...params] enables targeted cache invalidation on mutations"
  - "Pattern 5: Zod v4 + @hookform/resolvers v5 via standardSchemaResolver (not zodResolver)"

requirements-completed: [PRTA-01, PRTA-02]

# Metrics
duration: 19min
completed: 2026-03-23
---

# Phase 1 Plan 4: Portal — Tenant CRUD and Agent Designer Summary

**FastAPI portal API with bcrypt auth + Next.js 16 admin portal featuring Auth.js v5 login, tenant management, and a full-featured Agent Designer for defining AI employees**

## Performance

- **Duration:** 19 min
- **Started:** 2026-03-23T16:01:33Z
- **Completed:** 2026-03-23T16:20:09Z
- **Tasks:** 2
- **Files modified:** 25+ (new portal package + shared API)

## Accomplishments

- FastAPI portal router at /api/portal with full tenant CRUD, agent CRUD, and bcrypt auth endpoints
- Agent endpoints correctly set PostgreSQL RLS current_tenant_id context for policy compliance
- 38 integration tests prove all CRUD operations, validation, uniqueness constraints, and tenant isolation
- Next.js 16 portal builds cleanly with Auth.js v5 Credentials provider, proxy.ts auth guard
- Agent Designer with 6 grouped sections (Identity, Personality, Configuration, Capabilities, Escalation, Status) using employee-centric language throughout
- Tenant management: list with slug badges, create with auto-slug generation, edit, delete with cascade confirmation

## Task Commits

1. **Task 1: FastAPI portal API endpoints** - `7b348b9` (feat)
2. **Task 2: Next.js portal with Auth.js v5 and Agent Designer** - `cec7180` (feat)

## Files Created/Modified

- `/home/adelorenzo/repos/konstruct/packages/shared/shared/api/portal.py` — FastAPI portal router with all endpoints
- `/home/adelorenzo/repos/konstruct/packages/shared/shared/api/__init__.py` — exports portal_router
- `/home/adelorenzo/repos/konstruct/tests/integration/test_portal_tenants.py` — 23 tenant CRUD tests
- `/home/adelorenzo/repos/konstruct/tests/integration/test_portal_agents.py` — 15 agent + Agent Designer tests
- `/home/adelorenzo/repos/konstruct/packages/portal/proxy.ts` — Next.js 16 auth proxy
- `/home/adelorenzo/repos/konstruct/packages/portal/lib/auth.ts` — Auth.js v5 config
- `/home/adelorenzo/repos/konstruct/packages/portal/lib/api.ts` — typed fetch client
- `/home/adelorenzo/repos/konstruct/packages/portal/lib/queries.ts` — TanStack Query hooks
- `/home/adelorenzo/repos/konstruct/packages/portal/components/agent-designer.tsx` — Agent Designer form
- `/home/adelorenzo/repos/konstruct/packages/portal/components/tenant-form.tsx` — tenant form
- `/home/adelorenzo/repos/konstruct/packages/portal/components/nav.tsx` — sidebar nav

## Decisions Made

- **proxy.ts over middleware.ts:** Next.js 16 renamed `middleware.ts` to `proxy.ts`. The old name generates a deprecation warning. Used the new convention throughout.
- **standardSchemaResolver over zodResolver:** `@hookform/resolvers@5` no longer ships a dedicated `/zod` subpackage. It uses the Standard Schema protocol instead, and zod v4 implements Standard Schema. Using `standardSchemaResolver` from `@hookform/resolvers/standard-schema` is the correct integration pattern.
- **Route group (dashboard):** Moving tenant and agent routes into an `(dashboard)` route group shares the `QueryClientProvider` layout without adding `/dashboard` to the URL path. Clean separation of public (`(auth)`) and protected (`(dashboard)`) routes.
- **Auth.js v5 JWT strategy:** No `portal_sessions` table required for Phase 1. JWT tokens stored in httpOnly cookies via Auth.js session strategy.

## Deviations from Plan

### Auto-fixed Issues

**1. [Rule 1 - Bug] Agent portal endpoints set RLS tenant context**
- **Found during:** Task 1 (integration test execution)
- **Issue:** `POST /tenants/{tenant_id}/agents` raised `InsufficientPrivilegeError: new row violates row-level security policy for table "agents"` — the portal API connected as `konstruct_app` role but didn't set `app.current_tenant` session variable before inserting
- **Fix:** Added `current_tenant_id.set(tenant_id)` context manager around all agent DB operations in portal.py endpoints
- **Files modified:** packages/shared/shared/api/portal.py
- **Verification:** All 38 integration tests pass including agent create/read/update/delete
- **Committed in:** 7b348b9 (Task 1 commit)

**2. [Rule 3 - Blocking] Next.js 16 renamed middleware.ts to proxy.ts**
- **Found during:** Task 2 (reading Next.js 16 docs per CLAUDE.md/AGENTS.md instruction)
- **Issue:** Plan specified creating `middleware.ts`; Next.js 16 deprecates this in favor of `proxy.ts` with a `proxy()` export
- **Fix:** Created `proxy.ts` with `export async function proxy()` and `export const config` matcher
- **Files modified:** packages/portal/proxy.ts (created instead of middleware.ts)
- **Verification:** Build output shows `ƒ Proxy (Middleware)` — proxy is active
- **Committed in:** cec7180 (Task 2 commit)

**3. [Rule 3 - Blocking] zodResolver replaced with standardSchemaResolver**
- **Found during:** Task 2 (TypeScript build error)
- **Issue:** `@hookform/resolvers@5` removed the `/zod` subpackage — TypeScript error `Type 'Resolver<...string | undefined...>' is not assignable to type 'Resolver<...string...>'` because the new standard-schema protocol handles zod v4 output type inference differently
- **Fix:** Changed all imports from `zodResolver` (`@hookform/resolvers/zod`) to `standardSchemaResolver` (`@hookform/resolvers/standard-schema`) in login page, agent-designer, and tenant-form
- **Files modified:** app/(auth)/login/page.tsx, components/agent-designer.tsx, components/tenant-form.tsx
- **Verification:** `npm run build` exits 0 with no TypeScript errors
- **Committed in:** cec7180 (Task 2 commit)

**4. [Rule 3 - Blocking] Route group reorganization for QueryClientProvider scope**
- **Found during:** Task 2 (Next.js prerender error)
- **Issue:** `Error: No QueryClient set, use QueryClientProvider to set one` — `/agents` and `/tenants` routes were outside the `dashboard/layout.tsx` so they didn't get the QueryClientProvider wrapper
- **Fix:** Created `(dashboard)` route group with shared `layout.tsx`, moved `/dashboard`, `/tenants`, and `/agents` routes under it. URLs remain the same (`/tenants`, `/agents`, `/dashboard`) since route groups don't add path segments.
- **Files modified:** entire app route structure reorganized into (auth) and (dashboard) groups
- **Verification:** `npm run build` completes successfully with all 10 routes rendered
- **Committed in:** cec7180 (Task 2 commit)

---

**Total deviations:** 4 auto-fixed (1 bug, 3 blocking)
**Impact on plan:** All auto-fixes necessary for correctness and functionality. No scope creep. Deviations 2-4 are Next.js 16 + ecosystem version adaptations.

## Issues Encountered

- `create-next-app` initialized a nested `.git` repo inside `packages/portal/` — removed with `rm -rf packages/portal/.git` before committing to prevent git submodule confusion.

## User Setup Required

None — portal uses email/password auth against local DB, no external OAuth providers needed in Phase 1.

## Next Phase Readiness

- Portal API endpoints ready to receive connections from the gateway service (Plan 01-02, 01-03)
- Auth.js session ready; will need `AUTH_SECRET` env var set in production
- Agent Designer stores all fields needed for agent orchestration (Phase 2)
- Portal service added to docker-compose.yml — requires `npm run build` output or container build before running

## Self-Check: PASSED

All created files verified on disk. Both task commits (7b348b9, cec7180) confirmed in git log.

---
*Phase: 01-foundation*
*Completed: 2026-03-23*
