# Phase 5: Employee Design - Research **Researched:** 2026-03-24 **Domain:** Multi-path AI employee creation — wizard, templates, and Advanced mode refactor in Next.js 16 / React 19 **Confidence:** HIGH --- ## User Constraints (from CONTEXT.md) ### Locked Decisions - "New Employee" button presents three options: Templates / Guided Setup / Advanced - Templates: Card grid gallery of pre-built agents — one-click deploy - Guided Setup: 5-step wizard (Role → Persona → Tools → Channels → Escalation) - Advanced: Existing Agent Designer form — full manual control over all fields including system prompt - Labels: "Templates", "Guided Setup", "Advanced" — clear hierarchy from easiest to most control - 5 wizard steps: Role definition → Persona setup → Tool selection → Channel assignment → Escalation rules - System prompt auto-generated from wizard inputs — hidden from user (never shown during wizard) - Final step: Review summary card showing everything configured, user clicks "Deploy Employee" - After deploy: agent goes live on selected channels immediately - Wizard-created agents appear in Agent Designer for later customization - Templates stored as database seed data — platform admin can add/edit templates via portal - Card grid gallery with preview — each card shows: name, role description, included tools - "Preview" expands to show full configuration before deploying - V1 Templates (7+): Customer Support Rep, Sales Assistant, Office Manager, Project Coordinator, Financial Manager, Controller, Accountant - One-click deploy — no customization step before deployment - Auto-assigns to all connected channels for the tenant - User can find the deployed agent in the employee list and edit via Agent Designer later - Template is a snapshot — deploying creates an independent agent that doesn't track template changes - Agent Designer becomes the "Advanced" option for new employee creation - Also serves as the edit mode for all existing agents (regardless of how they were created) - Wizard-created and template-deployed agents are fully editable in Agent Designer - No functionality removed from Agent Designer — it remains the power-user tool ### Claude's Discretion - Wizard step UI design (stepper, cards, progress indicator) - Template card visual design - Review summary card layout - How wizard inputs map to system prompt construction - Template seed data format and migration approach - Whether templates get a dedicated DB table or reuse the agents table with a `is_template` flag ### Deferred Ideas (OUT OF SCOPE) None — discussion stayed within phase scope --- ## Phase Requirements | ID | Description | Research Support | |----|-------------|-----------------| | EMPL-01 | Multi-step wizard guides user through AI employee creation (role definition, persona, tools, channels, escalation rules) without requiring knowledge of system prompt format | Wizard component pattern using URL searchParams for step state; react-hook-form + zod for per-step validation; system prompt builder function maps wizard inputs to system_prompt field | | EMPL-02 | Pre-built agent templates (e.g., Customer Support Rep, Sales Assistant, Office Manager) available for one-click deployment with sensible defaults | Templates DB table with seed migration 007; new GET /templates and POST /templates/{id}/deploy API endpoints; TanStack Query hooks useTemplates and useDeployTemplate | | EMPL-03 | Template-deployed agents are immediately functional — respond in connected channels with the template's persona, tools, and escalation rules | Deploy endpoint calls existing POST /tenants/{tid}/agents + assigns all connected channels; channel_connections query provides auto-assignment targets | | EMPL-04 | Wizard and templates accessible to platform admins and customer admins (RBAC-enforced, not operators) | require_tenant_admin guard on all new creation endpoints — same guard already used on POST /tenants/{tid}/agents; EMPL-01/02 are creation paths, so existing guard applies | | EMPL-05 | Agents created via wizard or template appear in Agent Designer for further customization | All creation paths produce agents through the same POST /agents endpoint and ORM model; edit page at /agents/[id] already works for all agents | --- ## Summary Phase 5 adds three employee creation paths on top of the existing Agent CRUD infrastructure. The backend (FastAPI, SQLAlchemy, PostgreSQL) is largely complete — the Agent ORM model, create/update/delete endpoints, and RBAC guards all exist and need only minor additions. The primary new backend work is a `templates` table with seed data and a deploy endpoint that wraps the existing agent creation path. The frontend work is larger. The current `/agents/new` page routes directly to AgentDesigner. It must become a three-option entry screen. The 5-step wizard is a new client component using the same URL-searchParams step tracking pattern established by the onboarding wizard. The template gallery is a new card grid that reads from a new `/api/portal/templates` endpoint. The system prompt auto-generation for the wizard is a pure TypeScript function that assembles the stored fields (role, persona, tool list, escalation rules) into a coherent system prompt string — no AI call needed. **Primary recommendation:** Reuse the existing `OnboardingStepper` component pattern for the wizard stepper; store templates in a dedicated `agent_templates` table (cleaner than `is_template` flag, avoids polluting the agents list); use URL searchParams for wizard step state (established project pattern). --- ## Standard Stack ### Core (all already in use — no new installs needed) | Library | Version | Purpose | Why Standard | |---------|---------|---------|--------------| | Next.js | 16.2.1 | App Router, pages, routing | Project standard | | React | 19.2.4 | UI | Project standard | | react-hook-form | existing | Per-step form validation | Project standard (agentDesignerSchema pattern) | | zod | existing | Schema validation | Project standard (standardSchemaResolver) | | @hookform/resolvers | existing | standardSchemaResolver | Project standard — zodResolver dropped in v5 | | TanStack Query | existing | Server state, mutations | Project standard (useCreateAgent pattern) | | shadcn/ui | existing | Card, Button, Badge, Dialog, Select | Project standard | | Tailwind CSS | existing | Styling | Project standard | | FastAPI | existing | API endpoints | Project standard | | SQLAlchemy 2.0 | existing | ORM, templates table | Project standard | | Alembic | existing | Migration 007 for templates | Project standard | | pytest + pytest-asyncio + httpx | existing | Integration tests | Project standard | ### No new packages required All needed libraries are already installed. The wizard, template gallery, and system prompt builder are implemented using existing tools. --- ## Architecture Patterns ### Recommended File Structure (new files only) ``` packages/portal/ ├── app/(dashboard)/agents/ │ └── new/ │ ├── page.tsx # REPLACE: three-option entry screen │ ├── wizard/ │ │ └── page.tsx # NEW: 5-step wizard page │ └── templates/ │ └── page.tsx # NEW: template gallery page ├── components/ │ ├── employee-wizard.tsx # NEW: 5-step wizard component │ ├── employee-wizard-stepper.tsx # NEW: wizard progress indicator │ └── template-gallery.tsx # NEW: template card grid packages/shared/shared/ ├── api/ │ └── templates.py # NEW: GET /templates, POST /templates/{id}/deploy ├── models/ │ └── tenant.py # ADD: AgentTemplate ORM model └── prompts/ └── system_prompt_builder.py # NEW: wizard → system prompt construction migrations/versions/ └── 007_agent_templates.py # NEW: agent_templates table + seed data ``` ### Pattern 1: Three-Option Entry Screen The current `/agents/new/page.tsx` becomes a three-panel selection screen. Each option is a Card with a headline, description, and "Start" button. This is a client component. ```typescript // packages/portal/app/(dashboard)/agents/new/page.tsx "use client"; // Router push to three distinct destinations: // Templates → /agents/new/templates?tenant={id} // Guided Setup → /agents/new/wizard?tenant={id}&step=1 // Advanced → /agents/new/advanced?tenant={id} (or reuse existing AgentDesigner inline) ``` The "Advanced" path can either: - Route to a sub-path `/agents/new/advanced` that renders `` directly, OR - Replace `page.tsx` with a mode switch that renders the three-option screen when no `mode` param is set, and renders AgentDesigner when `?mode=advanced` **Recommendation:** Sub-pages (`/agents/new/wizard`, `/agents/new/templates`) are cleaner — each is an independent route with its own URL. The existing `/agents/new` page becomes the selector. Advanced mode moves to `/agents/new/advanced`. ### Pattern 2: 5-Step Wizard with URL State The onboarding wizard at `/onboarding` uses `?step=1|2|3` in URL searchParams. The employee wizard follows the same pattern — established, browser-refresh safe, shareable. The wizard page is an async server component that reads `searchParams` (Promise in Next.js 15+). The step content components are client components. ```typescript // packages/portal/app/(dashboard)/agents/new/wizard/page.tsx // Server component (no "use client") interface WizardPageProps { searchParams: Promise<{ step?: string; tenant?: string }>; } export default async function WizardPage({ searchParams }: WizardPageProps) { const params = await searchParams; const step = parseInt(params.step ?? "1", 10); const tenantId = params.tenant_id ?? ""; // ...render EmployeeWizardStepper + step content } ``` Step navigation uses `router.push` with updated searchParams: ```typescript router.push(`/agents/new/wizard?tenant=${tenantId}&step=${nextStep}`); ``` ### Pattern 3: Wizard State — React useState (not URL) The wizard accumulates data across 5 steps. The step page contains the stepper and renders one step component at a time. Step data is held in a client-component parent via `useState` — NOT in URL params (persona text, tool lists would pollute the URL). ```typescript // employee-wizard.tsx — client component "use client"; interface WizardData { role: string; roleTitle: string; persona: string; tool_assignments: string[]; channel_ids: string[]; // IDs of selected channels to assign escalation_rules: { condition: string; action: string }[]; } // Parent holds state, passes down to each step component const [wizardData, setWizardData] = useState>({}); ``` The wizard page itself is a server component that renders `` which is a client component managing all state. ### Pattern 4: System Prompt Builder A pure TypeScript/Python function assembles wizard inputs into a system prompt. No LLM call — just string templating. ```typescript // lib/system-prompt-builder.ts export function buildSystemPrompt(data: { name: string; role: string; persona: string; tool_assignments: string[]; escalation_rules: { condition: string; action: string }[]; }): string { const toolsSection = data.tool_assignments.length > 0 ? `\n\nYou have access to the following tools: ${data.tool_assignments.join(", ")}.` : ""; const escalationSection = data.escalation_rules.length > 0 ? `\n\nEscalation rules:\n${data.escalation_rules.map(r => `- If ${r.condition}: ${r.action}`).join("\n")}` : ""; const aiClause = "\n\nWhen directly asked if you are an AI, always disclose that you are an AI assistant."; return `You are ${data.name}, ${data.role}.\n\n${data.persona}${toolsSection}${escalationSection}${aiClause}`; } ``` The AI transparency clause (unconditional AI disclosure) is a locked project decision from Phase 1. It must be included in all auto-generated system prompts. ### Pattern 5: Template DB Table (Dedicated Table) The CONTEXT.md leaves the template storage approach to Claude's discretion. A dedicated `agent_templates` table is cleaner than an `is_template` flag on the agents table: - Templates are never tenant-scoped (global, created by platform admin) - They should not appear in tenant agent lists - They have additional metadata fields (description, category, preview) - No RLS needed — templates are read-only for all users, write for platform admin ```sql -- agent_templates table (migration 007) CREATE TABLE agent_templates ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL, role TEXT NOT NULL, description TEXT NOT NULL DEFAULT '', -- shown in card preview category TEXT NOT NULL DEFAULT 'general', persona TEXT NOT NULL DEFAULT '', system_prompt TEXT NOT NULL DEFAULT '', model_preference TEXT NOT NULL DEFAULT 'quality', tool_assignments JSONB NOT NULL DEFAULT '[]', escalation_rules JSONB NOT NULL DEFAULT '[]', is_active BOOLEAN NOT NULL DEFAULT TRUE, sort_order INTEGER NOT NULL DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); ``` Platform admin CRUD uses `require_platform_admin` guard. All users can GET templates (no auth required beyond being a portal member). ### Pattern 6: Template Deploy Endpoint Deploy calls existing agent creation logic + auto-assigns all connected channels. ```python # POST /api/portal/templates/{template_id}/deploy # Body: { tenant_id: UUID } # Guard: require_tenant_admin # Logic: # 1. Fetch template by ID # 2. Create Agent from template fields (snapshot — independent copy) # 3. Return AgentResponse ``` Auto-assignment to connected channels: the deploy endpoint queries `channel_connections` for the tenant and stores the channel IDs. For v1, the agent is created and marked active — the channel assignment means the agent responds to all messages in those channels (the existing routing already assigns by tenant, not per-agent channel list). No additional channel-agent join table is needed for v1. ### Pattern 7: Wizard Step Components Each step is a separate component file under `components/wizard-steps/`: ``` components/wizard-steps/ ├── step-role.tsx # Name + Job Title inputs ├── step-persona.tsx # Persona textarea (plain language) ├── step-tools.tsx # Tool multi-select (badge chips, same as AgentDesigner) ├── step-channels.tsx # Channel multi-select from connected channels ├── step-escalation.tsx # Escalation rules (simplified compared to AgentDesigner) └── step-review.tsx # Summary card + "Deploy Employee" button ``` Each step receives `wizardData` and `onNext: (updates: Partial) => void`. The review step receives the complete `wizardData` and calls the createAgent mutation. ### Anti-Patterns to Avoid - **Storing wizard state in URL params:** Role name is fine, but persona text and tool lists would make URLs unreadable and hit browser URL length limits. Keep multi-field state in React state. - **Creating a new agent creation endpoint for wizard:** The wizard uses the existing `POST /tenants/{tid}/agents` endpoint — it just pre-fills `system_prompt` from the builder function. - **`is_template` flag on the agents table:** Templates are global platform data, not tenant agents. A separate table avoids polluting agent lists and avoids RLS complications. - **Template tracking changes after deploy:** The CONTEXT.md explicitly states deployed agents are independent snapshots. Do not add foreign key from agents back to templates. - **Showing the system_prompt field in the wizard UI:** Locked decision — system prompt is hidden during wizard. Only visible in Agent Designer (Advanced mode). --- ## Don't Hand-Roll | Problem | Don't Build | Use Instead | Why | |---------|-------------|-------------|-----| | Step progress indicator | Custom stepper from scratch | Adapt existing `OnboardingStepper` component | Already built, same visual style, just update step count and labels | | Form validation | Custom validation logic | react-hook-form + zod (per-step schemas) | Established project pattern, handles error display | | Server state | Custom fetch/cache | TanStack Query `useTemplates`, `useDeployTemplate` hooks | Established pattern, handles loading/error states | | Tool chip UI | Custom chip component | Badge + X button (same as AgentDesigner tool_assignments section) | Already implemented, copy the pattern | | Channel selection | Custom channel list | useChannelConnections hook + checkboxes | Hook already exists in queries.ts | --- ## Common Pitfalls ### Pitfall 1: searchParams is a Promise in Next.js 15+ (project uses Next.js 16) **What goes wrong:** Accessing `searchParams.step` directly causes a build error or runtime warning. **Why it happens:** Next.js 15 made `searchParams` a Promise in page components. Confirmed in the project's STATE.md and existing onboarding page. **How to avoid:** In server components, `await searchParams`. In client components, use `useSearchParams()` from `next/navigation`. **Warning signs:** TypeScript errors on `.step` access without await. ### Pitfall 2: Wizard state lost on browser refresh **What goes wrong:** User refreshes midway through wizard, loses all entered data. **Why it happens:** React useState is ephemeral. **How to avoid:** For the current project's needs (5-step wizard completing in < 5 minutes), this is acceptable UX. Add a warning comment in the component. Do NOT try to serialize wizard state into URL params (too complex). If the user refreshes, they restart — this is fine. **Warning signs:** Temptation to serialize complex state to sessionStorage or URL. ### Pitfall 3: Template deploy creates inactive agent **What goes wrong:** Deployed agent is inactive by default, not responding in channels. **Why it happens:** `is_active` defaults to `True` in the ORM, but code might explicitly set False. **How to avoid:** Template deploy always creates agents with `is_active=True`. The phase 3 decision ("agent goes live automatically, is_active true by default") applies here. ### Pitfall 4: Wizard channel step — no connected channels **What goes wrong:** User reaches the Channels step but the tenant has no connected channels yet. **Why it happens:** Channel connection (Slack/WhatsApp) is a separate onboarding step. **How to avoid:** The Channels step should handle the empty state gracefully — show a message "No channels connected yet. Your employee will be deployed and can be assigned to channels later." Allow the step to be skipped with no channels selected. ### Pitfall 5: Template gallery visible to customer_operator **What goes wrong:** Operators can browse templates but cannot deploy (403 on the deploy endpoint). Confusing UX. **Why it happens:** EMPL-04 restricts wizard and template deployment to platform_admin and customer_admin. **How to avoid:** Check caller role before rendering the "New Employee" button (same RBAC check already used for nav items). The three-option screen should only be reachable by admins. If an operator somehow reaches it, the deploy API will return 403 and the UI should handle that error. ### Pitfall 6: System prompt builder missing AI transparency clause **What goes wrong:** Wizard-generated system prompts omit the mandatory AI disclosure clause. **Why it happens:** The disclosure is hardcoded into the default system prompt template but easy to forget when building the auto-generator. **How to avoid:** The system prompt builder function always appends: "When directly asked if you are an AI, always disclose that you are an AI assistant." This is a non-negotiable per Phase 1 design decision. --- ## Code Examples ### Existing stepper pattern (adapt for 5-step wizard) The `OnboardingStepper` component at `packages/portal/components/onboarding-stepper.tsx` accepts `currentStep: number` and a `StepInfo[]` array. For the employee wizard, create `EmployeeWizardStepper` with the same structure but 5 steps: ```typescript // Source: packages/portal/components/onboarding-stepper.tsx (existing) export const WIZARD_STEPS: StepInfo[] = [ { number: 1, label: "Role", description: "What will they do?" }, { number: 2, label: "Persona", description: "How will they behave?" }, { number: 3, label: "Tools", description: "What can they use?" }, { number: 4, label: "Channels", description: "Where will they work?" }, { number: 5, label: "Escalation", description: "When to hand off?" }, ]; // + step 6 (Review) is not a numbered wizard step — it's the final deploy screen ``` ### Existing createAgent mutation (wizard and template deploy both use this) ```typescript // Source: packages/portal/lib/queries.ts (existing) const createAgent = useCreateAgent(); await createAgent.mutateAsync({ tenantId: tenantId, data: { name: wizardData.name, role: wizardData.roleTitle, persona: wizardData.persona, system_prompt: buildSystemPrompt(wizardData), // auto-generated model_preference: "quality", tool_assignments: wizardData.tool_assignments, escalation_rules: wizardData.escalation_rules, is_active: true, }, }); ``` ### Alembic migration pattern for seed data (migration 007) ```python # Source: migrations/versions/006_rbac_roles.py pattern revision: str = "007" down_revision: Union[str, None] = "006" def upgrade() -> None: op.create_table( "agent_templates", sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True, ...), sa.Column("name", sa.String(255), nullable=False), ... ) # Seed data INSERT op.execute(""" INSERT INTO agent_templates (id, name, role, description, ...) VALUES (gen_random_uuid(), 'Customer Support Rep', 'Customer Support Representative', ...), ... """) ``` ### Channel auto-assignment on template deploy ```python # POST /api/portal/templates/{template_id}/deploy async def deploy_template(template_id, body, caller, session): template = await get_template_or_404(template_id, session) # Create agent (snapshot of template) agent = Agent( tenant_id=body.tenant_id, name=template.name, role=template.role, persona=template.persona, system_prompt=template.system_prompt, model_preference=template.model_preference, tool_assignments=template.tool_assignments, escalation_rules=template.escalation_rules, is_active=True, ) session.add(agent) await session.commit() # Note: channel routing is tenant-scoped, not per-agent in v1. # Agent responds to all channels connected to the tenant automatically. return AgentResponse.from_orm(agent) ``` --- ## State of the Art | Old Approach | Current Approach | When Changed | Impact | |--------------|------------------|--------------|--------| | zodResolver from @hookform/resolvers | standardSchemaResolver | hookform/resolvers v5 (project already uses this) | Must use standardSchemaResolver, not zodResolver | | `searchParams` as sync object | `searchParams` as Promise | Next.js 15+ (project on 16.2.1) | Must `await searchParams` in server components, use `useSearchParams()` in client components | | `params` as sync object | `params` as Promise | Next.js 15+ (project on 16.2.1) | Must `use(params)` in client components (see existing /agents/[id]/page.tsx) | | middleware.ts | proxy.ts | Next.js 16 (renamed) | Project already uses proxy.ts | --- ## Open Questions 1. **Wizard Channels Step — what exactly gets "assigned"?** - What we know: The deploy decision says "auto-assigns to all connected channels." The existing agent routing is tenant-scoped — all tenant agents share the channel and respond by agent selection logic. - What's unclear: Is there a per-agent channel assignment in the ORM/routing layer, or is it purely tenant-level? - Recommendation: Audit the orchestrator routing logic before planning. If per-agent channel assignment doesn't exist in the DB schema, the Channels step in the wizard becomes an informational step ("Your employee will be active in these channels") rather than a configuration step. Do not add a channel-agent join table in this phase. 2. **Template CRUD for platform admin — new portal page or inline?** - What we know: Templates are stored as DB seed data. Platform admin should be able to add/edit. - What's unclear: Context says "platform admin can add/edit templates via portal" but gives no UI spec. - Recommendation: For Phase 5, templates are read-only via seed data. Full template CRUD UI can be a v2 feature. The seed migration covers the 7 V1 templates. 3. **Wizard "Deploy Employee" step — return URL after success?** - What we know: Current createAgent mutations redirect to `/agents` on success. - Recommendation: Redirect to `/agents/{newAgent.id}?tenant={tenantId}` (the edit page) to confirm deployment and offer immediate customization. This satisfies EMPL-05 (agent appears in Agent Designer). --- ## Validation Architecture ### Test Framework | Property | Value | |----------|-------| | Framework | pytest 8.x + pytest-asyncio + httpx | | Config file | `pyproject.toml` (root) | | Quick run command | `pytest tests/unit -x` | | Full suite command | `pytest tests/ -x` | ### Phase Requirements → Test Map | Req ID | Behavior | Test Type | Automated Command | File Exists? | |--------|----------|-----------|-------------------|-------------| | EMPL-01 | Wizard creates agent with auto-generated system_prompt | unit | `pytest tests/unit/test_system_prompt_builder.py -x` | ❌ Wave 0 | | EMPL-01 | Wizard agent creation hits POST /tenants/{tid}/agents (existing endpoint) | integration | `pytest tests/integration/test_portal_agents.py -x` | ✅ existing | | EMPL-02 | GET /api/portal/templates returns template list | integration | `pytest tests/integration/test_templates.py -x` | ❌ Wave 0 | | EMPL-02 | Template deploy creates independent agent snapshot | integration | `pytest tests/integration/test_templates.py::test_deploy_template -x` | ❌ Wave 0 | | EMPL-03 | Deployed agent is_active=True and correct fields from template | integration | `pytest tests/integration/test_templates.py::test_deployed_agent_is_active -x` | ❌ Wave 0 | | EMPL-04 | Template deploy blocked for customer_operator (403) | integration | `pytest tests/integration/test_templates.py::test_deploy_template_rbac -x` | ❌ Wave 0 | | EMPL-04 | Wizard agent creation blocked for customer_operator (403) | integration | `pytest tests/integration/test_portal_rbac.py -x` | ✅ existing | | EMPL-05 | Wizard-created agent has correct fields (accessible via GET /agents/{id}) | integration | `pytest tests/integration/test_portal_agents.py -x` | ✅ existing | ### Sampling Rate - **Per task commit:** `pytest tests/unit -x` - **Per wave merge:** `pytest tests/ -x` - **Phase gate:** Full suite green before `/gsd:verify-work` ### Wave 0 Gaps - [ ] `tests/unit/test_system_prompt_builder.py` — covers EMPL-01 (system prompt construction) - [ ] `tests/integration/test_templates.py` — covers EMPL-02, EMPL-03, EMPL-04 *(Frontend wizard UX is manual-only: no automated test framework for Next.js components is configured in this project)* --- ## Sources ### Primary (HIGH confidence) - `/home/adelorenzo/repos/konstruct/packages/portal/AGENTS.md` — Next.js version caveat; confirmed Next.js 16.2.1, React 19.2.4 - `/home/adelorenzo/repos/konstruct/packages/portal/node_modules/next/dist/docs/01-app/03-api-reference/04-functions/use-search-params.md` — searchParams is read-only URLSearchParams in client components - `/home/adelorenzo/repos/konstruct/packages/portal/node_modules/next/dist/docs/01-app/03-api-reference/04-functions/use-router.md` — router.push/replace API confirmed - `/home/adelorenzo/repos/konstruct/packages/portal/node_modules/next/dist/docs/01-app/02-guides/forms.md` — Server Actions form patterns (not used for this phase — TanStack Query pattern preferred per project conventions) - `/home/adelorenzo/repos/konstruct/packages/portal/components/onboarding-stepper.tsx` — OnboardingStepper pattern to reuse - `/home/adelorenzo/repos/konstruct/packages/portal/app/(dashboard)/onboarding/page.tsx` — `searchParams: Promise<{...}>` server component pattern confirmed - `/home/adelorenzo/repos/konstruct/packages/shared/shared/models/tenant.py` — Agent ORM model fields confirmed - `/home/adelorenzo/repos/konstruct/packages/shared/shared/api/portal.py` — `require_tenant_admin` guard on POST /agents confirmed - `/home/adelorenzo/repos/konstruct/.planning/STATE.md` — Project-wide architectural decisions ### Secondary (MEDIUM confidence) - `/home/adelorenzo/repos/konstruct/.planning/phases/05-employee-design/05-CONTEXT.md` — User decisions (authoritative for this phase) --- ## Metadata **Confidence breakdown:** - Standard stack: HIGH — all libraries verified from existing source files - Architecture: HIGH — patterns derived from existing onboarding wizard and agent designer code in the project - Pitfalls: HIGH — searchParams/params as Promise pitfalls confirmed from Next.js 16 docs; other pitfalls from existing STATE.md decisions - System prompt builder: HIGH — pure function, no external dependencies - Template DB design: MEDIUM — dedicated table recommendation is reasoned but not verified against a specific external source **Research date:** 2026-03-24 **Valid until:** 2026-04-24 (stable stack — Next.js 16, no breaking changes expected)