# 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>
## 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
</user_constraints>

---

<phase_requirements>
## 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 |
</phase_requirements>

---

## 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 `<AgentDesigner>` 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<Partial<WizardData>>({});
```

The wizard page itself is a server component that renders `<EmployeeWizard tenantId={tenantId} initialStep={step} />` 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<WizardData>) => 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)
