--- 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*