docs(phase-5): research employee design phase

This commit is contained in:
2026-03-24 20:04:47 -06:00
parent 40eb3106ab
commit 84d8059eac

View File

@@ -0,0 +1,532 @@
# 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)