Compare commits

...

3 Commits

6 changed files with 1330 additions and 17 deletions

View File

@@ -93,6 +93,23 @@ Plans:
- [ ] 04-02-PLAN.md — Portal RBAC integration: Auth.js JWT role claims, proxy role redirects, role-filtered nav, tenant switcher, impersonation banner, invite acceptance page, user management pages
- [ ] 04-03-PLAN.md — Wire RBAC guards to all existing API endpoints, impersonation audit logging, integration tests, human verification checkpoint
### Phase 5: Employee Design
**Goal**: Operators and customer admins can create AI employees through a guided wizard that walks them through role definition, persona setup, tool selection, and channel assignment — or deploy instantly from a library of pre-built agent templates
**Depends on**: Phase 4
**Requirements**: EMPL-01, EMPL-02, EMPL-03, EMPL-04, EMPL-05
**Success Criteria** (what must be TRUE):
1. An operator can create a fully configured AI employee by completing a multi-step wizard without needing to understand the underlying system prompt format
2. Pre-built agent templates (e.g., Customer Support Rep, Sales Assistant, Office Manager) are available for one-click deployment with sensible defaults
3. A template-deployed agent is immediately functional — responds in connected channels with the template's persona, tools, and escalation rules
4. The wizard and templates are accessible to both platform admins and customer admins (respecting RBAC)
5. Created agents appear in the Agent Designer for further customization after initial setup
**Plans**: 3 plans
Plans:
- [ ] 05-01-PLAN.md — Backend: AgentTemplate model, migration 007 with 7 seed templates, template list/deploy API, system prompt builder, unit + integration tests
- [ ] 05-02-PLAN.md — Frontend: three-option entry screen, template gallery with one-click deploy, 5-step wizard (Role/Persona/Tools/Channels/Escalation), Advanced mode relocation
- [ ] 05-03-PLAN.md — Human verification: test all three creation paths, RBAC enforcement, system prompt auto-generation
## Progress
**Execution Order:**
@@ -104,7 +121,7 @@ Phases execute in numeric order: 1 -> 2 -> 3 -> 4 -> 5
| 2. Agent Features | 6/6 | Complete | 2026-03-24 |
| 3. Operator Experience | 5/5 | Complete | 2026-03-24 |
| 4. RBAC | 3/3 | Complete | 2026-03-24 |
| 5. Employee Design | 0/0 | Not started | - |
| 5. Employee Design | 0/3 | Not started | - |
---
@@ -112,21 +129,6 @@ Phases execute in numeric order: 1 -> 2 -> 3 -> 4 -> 5
**LLM-03 conflict resolved:** BYO API keys confirmed in v1 scope per user decision during Phase 3 context gathering. Implemented via Fernet encryption in Phase 3.
### Phase 5: Employee Design
**Goal**: Operators and customer admins can create AI employees through a guided wizard that walks them through role definition, persona setup, tool selection, and channel assignment — or deploy instantly from a library of pre-built agent templates
**Depends on**: Phase 4
**Requirements**: EMPL-01, EMPL-02, EMPL-03, EMPL-04, EMPL-05
**Success Criteria** (what must be TRUE):
1. An operator can create a fully configured AI employee by completing a multi-step wizard without needing to understand the underlying system prompt format
2. Pre-built agent templates (e.g., Customer Support Rep, Sales Assistant, Office Manager) are available for one-click deployment with sensible defaults
3. A template-deployed agent is immediately functional — responds in connected channels with the template's persona, tools, and escalation rules
4. The wizard and templates are accessible to both platform admins and customer admins (respecting RBAC)
5. Created agents appear in the Agent Designer for further customization after initial setup
**Plans**: 0 plans
Plans:
- [ ] TBD (run /gsd:plan-phase 5 to break down)
---
*Roadmap created: 2026-03-23*
*Coverage: 25/25 v1 requirements + 6 RBAC requirements mapped*
*Coverage: 25/25 v1 requirements + 6 RBAC requirements + 5 Employee Design requirements mapped*

View File

@@ -0,0 +1,257 @@
---
phase: 05-employee-design
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- packages/shared/shared/models/tenant.py
- packages/shared/shared/api/templates.py
- packages/shared/shared/prompts/system_prompt_builder.py
- migrations/versions/007_agent_templates.py
- packages/gateway/main.py
- tests/unit/test_system_prompt_builder.py
- tests/integration/test_templates.py
autonomous: true
requirements: [EMPL-02, EMPL-03, EMPL-04]
must_haves:
truths:
- "GET /api/portal/templates returns a list of 7+ pre-built agent templates"
- "POST /api/portal/templates/{id}/deploy creates an independent agent snapshot with is_active=True"
- "Template deploy is blocked for customer_operator (403)"
- "System prompt builder produces a coherent prompt including AI transparency clause"
artifacts:
- path: "packages/shared/shared/models/tenant.py"
provides: "AgentTemplate ORM model"
contains: "class AgentTemplate"
- path: "packages/shared/shared/api/templates.py"
provides: "Template list + deploy endpoints"
exports: ["templates_router"]
- path: "packages/shared/shared/prompts/system_prompt_builder.py"
provides: "System prompt auto-generation from wizard inputs"
exports: ["build_system_prompt"]
- path: "migrations/versions/007_agent_templates.py"
provides: "agent_templates table with 7 seed templates"
contains: "agent_templates"
- path: "tests/unit/test_system_prompt_builder.py"
provides: "Unit tests for prompt builder"
- path: "tests/integration/test_templates.py"
provides: "Integration tests for template API"
key_links:
- from: "packages/shared/shared/api/templates.py"
to: "packages/shared/shared/models/tenant.py"
via: "AgentTemplate ORM model import"
pattern: "from shared\\.models\\.tenant import.*AgentTemplate"
- from: "packages/shared/shared/api/templates.py"
to: "packages/shared/shared/models/tenant.py"
via: "Agent ORM model for deploy snapshot"
pattern: "Agent\\("
- from: "packages/gateway/main.py"
to: "packages/shared/shared/api/templates.py"
via: "Router mount"
pattern: "templates_router"
---
<objective>
Backend foundation for the Employee Design phase: AgentTemplate ORM model, database migration with 7 seed templates, template list + deploy API endpoints, system prompt builder function, and comprehensive tests.
Purpose: Provide the API layer that the frontend template gallery and wizard will consume. Templates are global (not tenant-scoped), readable by all authenticated users, deployable by tenant admins only.
Output: Working API at /api/portal/templates (GET list, GET detail, POST deploy), system prompt builder module, migration 007, unit + integration tests.
</objective>
<execution_context>
@/home/adelorenzo/.claude/get-shit-done/workflows/execute-plan.md
@/home/adelorenzo/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/05-employee-design/05-CONTEXT.md
@.planning/phases/05-employee-design/05-RESEARCH.md
@.planning/phases/05-employee-design/05-VALIDATION.md
<interfaces>
<!-- Key types and contracts the executor needs. Extracted from codebase. -->
From packages/shared/shared/models/tenant.py:
```python
class Base(DeclarativeBase):
pass
class Agent(Base):
__tablename__ = "agents"
id: Mapped[uuid.UUID]
tenant_id: Mapped[uuid.UUID] # FK to tenants.id
name: Mapped[str]
role: Mapped[str]
persona: Mapped[str]
system_prompt: Mapped[str]
model_preference: Mapped[str] # quality | balanced | economy | local
tool_assignments: Mapped[list[Any]] # JSON
escalation_rules: Mapped[list[Any]] # JSON
escalation_assignee: Mapped[str | None]
natural_language_escalation: Mapped[bool]
is_active: Mapped[bool] # default True
budget_limit_usd: Mapped[float | None]
created_at: Mapped[datetime]
updated_at: Mapped[datetime]
```
From packages/shared/shared/api/portal.py:
```python
portal_router = APIRouter(prefix="/api/portal", tags=["portal"])
class AgentCreate(BaseModel):
name: str
role: str
persona: str = ""
system_prompt: str = ""
model_preference: str = "quality"
tool_assignments: list[str] = []
escalation_rules: list[dict] = []
is_active: bool = True
class AgentResponse(BaseModel):
id: str; tenant_id: str; name: str; role: str; persona: str
system_prompt: str; model_preference: str; tool_assignments: list[str]
escalation_rules: list[dict]; is_active: bool; budget_limit_usd: float | None
created_at: datetime; updated_at: datetime
```
From packages/shared/shared/api/rbac.py:
```python
async def require_platform_admin(caller: PortalCaller) -> PortalCaller
async def require_tenant_admin(caller: PortalCaller) -> PortalCaller
async def require_tenant_member(caller: PortalCaller) -> PortalCaller
```
From packages/gateway/main.py (router mounting pattern):
```python
from shared.api.portal import portal_router
app.include_router(portal_router)
# All Phase 3 routers mounted similarly
```
Latest migration: 006_rbac_roles.py (next is 007)
</interfaces>
</context>
<tasks>
<task type="auto" tdd="true">
<name>Task 1: AgentTemplate model, migration 007, system prompt builder, and tests</name>
<files>
packages/shared/shared/models/tenant.py,
migrations/versions/007_agent_templates.py,
packages/shared/shared/prompts/__init__.py,
packages/shared/shared/prompts/system_prompt_builder.py,
tests/unit/test_system_prompt_builder.py
</files>
<behavior>
- build_system_prompt({name: "Mara", role: "Customer Support", persona: "Friendly and helpful", tool_assignments: ["knowledge_base_search"], escalation_rules: [{"condition": "billing_dispute AND attempts > 2", "action": "handoff_human"}]}) produces a string containing "You are Mara", "Customer Support", "Friendly and helpful", "knowledge_base_search", the escalation rule text, AND the AI transparency clause "When directly asked if you are an AI, always disclose that you are an AI assistant."
- build_system_prompt with empty tools and empty escalation_rules omits those sections (no "tools:" or "Escalation" text)
- build_system_prompt with only name and role still produces valid prompt with AI transparency clause
</behavior>
<action>
1. Add `AgentTemplate` ORM model to `packages/shared/shared/models/tenant.py`:
- NOT tenant-scoped (no tenant_id, no RLS)
- Fields: id (UUID PK), name (String 255), role (String 255), description (Text, shown in card preview), category (String 100, default "general"), persona (Text), system_prompt (Text), model_preference (String 50, default "quality"), tool_assignments (JSON, default []), escalation_rules (JSON, default []), is_active (Boolean, default True), sort_order (Integer, default 0), created_at (DateTime tz, server_default now())
- Add `__repr__` method
2. Create migration `007_agent_templates.py`:
- down_revision = "006"
- Create `agent_templates` table matching the ORM model
- Seed 7 templates with INSERT. Each template needs: name, role, description (2-3 sentences for the card preview), category, persona (paragraph describing communication style), system_prompt (full system prompt with AI transparency clause), model_preference "quality", tool_assignments (JSON array of relevant tool names), escalation_rules (JSON array of {condition, action} objects)
- Templates:
a. Customer Support Rep — category "support", tools: knowledge_base_search, zendesk_ticket_lookup, zendesk_ticket_create. Escalation: billing_dispute AND attempts > 2 -> handoff_human, sentiment < -0.7 -> handoff_human
b. Sales Assistant — category "sales", tools: knowledge_base_search, calendar_book. Escalation: pricing_negotiation AND attempts > 3 -> handoff_human
c. Office Manager — category "operations", tools: knowledge_base_search, calendar_book. Escalation: hr_complaint -> handoff_human
d. Project Coordinator — category "operations", tools: knowledge_base_search. Escalation: deadline_missed -> handoff_human
e. Financial Manager — category "finance", tools: knowledge_base_search. Escalation: large_transaction AND amount > threshold -> handoff_human
f. Controller — category "finance", tools: knowledge_base_search. Escalation: budget_exceeded -> handoff_human
g. Accountant — category "finance", tools: knowledge_base_search. Escalation: invoice_discrepancy AND amount > threshold -> handoff_human
3. Create `packages/shared/shared/prompts/__init__.py` (empty) and `system_prompt_builder.py`:
- Function `build_system_prompt(name: str, role: str, persona: str = "", tool_assignments: list[str] | None = None, escalation_rules: list[dict] | None = None) -> str`
- Assembles: "You are {name}, {role}.\n\n{persona}" + optional tools section + optional escalation section + ALWAYS the AI transparency clause
- AI transparency clause (non-negotiable, per Phase 1 decision): "\n\nWhen directly asked if you are an AI, always disclose that you are an AI assistant."
4. Write unit tests in `tests/unit/test_system_prompt_builder.py`:
- Test full prompt with all fields populated (name, role, persona, tools, escalation)
- Test minimal prompt (name + role only) still includes AI clause
- Test empty tools/escalation omit those sections
- Test AI transparency clause is always present
</action>
<verify>
<automated>cd /home/adelorenzo/repos/konstruct && python -m pytest tests/unit/test_system_prompt_builder.py -x -v</automated>
</verify>
<done>AgentTemplate ORM model exists in tenant.py, migration 007 creates table with 7 seed templates, build_system_prompt function passes all unit tests including AI transparency clause verification</done>
</task>
<task type="auto">
<name>Task 2: Template API endpoints (list, detail, deploy) with RBAC and integration tests</name>
<files>
packages/shared/shared/api/templates.py,
packages/gateway/main.py,
tests/integration/test_templates.py
</files>
<action>
1. Create `packages/shared/shared/api/templates.py` with a new `templates_router = APIRouter(prefix="/api/portal", tags=["templates"])`:
Pydantic schemas:
- `TemplateResponse(BaseModel)`: id (str), name, role, description, category, persona, system_prompt, model_preference, tool_assignments (list[str]), escalation_rules (list[dict]), is_active (bool), sort_order (int), created_at (datetime)
- `TemplateDeployRequest(BaseModel)`: tenant_id (str, UUID format)
- `TemplateDeployResponse(BaseModel)`: agent (AgentResponse from portal.py) — the newly created agent
Endpoints:
a. `GET /api/portal/templates` — returns list[TemplateResponse]. Guard: `require_tenant_member` (any authenticated portal user can browse). Query: `SELECT * FROM agent_templates WHERE is_active = True ORDER BY sort_order, name`. No RLS needed (templates are global).
b. `GET /api/portal/templates/{template_id}` — returns TemplateResponse. Guard: `require_tenant_member`. 404 if not found or inactive.
c. `POST /api/portal/templates/{template_id}/deploy` — Guard: `require_tenant_admin` (per EMPL-04). Body: TemplateDeployRequest with tenant_id.
- Fetch template by ID (404 if not found)
- Set RLS context to body.tenant_id (same pattern as create_agent in portal.py)
- Create Agent from template fields: name, role, persona, system_prompt, model_preference, tool_assignments, escalation_rules, is_active=True
- Commit and return TemplateDeployResponse with the new agent
2. Mount `templates_router` in `packages/gateway/main.py` alongside existing routers:
```python
from shared.api.templates import templates_router
app.include_router(templates_router)
```
3. Write integration tests in `tests/integration/test_templates.py`:
- `test_list_templates` — GET returns 200 with 7+ templates (seeded by migration)
- `test_get_template_detail` — GET single template returns correct fields
- `test_deploy_template` — POST deploy returns 201 with new agent, agent has is_active=True, agent fields match template (snapshot)
- `test_deploy_template_rbac` — POST deploy as customer_operator returns 403
- `test_deploy_template_not_found` — POST deploy with invalid UUID returns 404
- Follow the existing test pattern from `tests/integration/test_portal_rbac.py` for RBAC header injection (X-Portal-User-Id, X-Portal-User-Role, X-Portal-Tenant-Id headers)
</action>
<verify>
<automated>cd /home/adelorenzo/repos/konstruct && python -m pytest tests/integration/test_templates.py -x -v</automated>
</verify>
<done>GET /api/portal/templates returns 7 seeded templates, POST deploy creates an independent agent snapshot with is_active=True, customer_operator gets 403 on deploy, all integration tests pass</done>
</task>
</tasks>
<verification>
1. `pytest tests/unit/test_system_prompt_builder.py -x` passes
2. `pytest tests/integration/test_templates.py -x` passes
3. `pytest tests/ -x` full suite still green (no regressions)
</verification>
<success_criteria>
- AgentTemplate table exists with 7 seed templates
- GET /api/portal/templates returns all active templates
- POST /api/portal/templates/{id}/deploy creates agent snapshot with is_active=True
- Deploy blocked for customer_operator (403)
- System prompt builder produces valid prompts with AI transparency clause
- All unit and integration tests pass
</success_criteria>
<output>
After completion, create `.planning/phases/05-employee-design/05-01-SUMMARY.md`
</output>

View File

@@ -0,0 +1,345 @@
---
phase: 05-employee-design
plan: 02
type: execute
wave: 2
depends_on: ["05-01"]
files_modified:
- packages/portal/app/(dashboard)/agents/new/page.tsx
- packages/portal/app/(dashboard)/agents/new/advanced/page.tsx
- packages/portal/app/(dashboard)/agents/new/templates/page.tsx
- packages/portal/app/(dashboard)/agents/new/wizard/page.tsx
- packages/portal/components/employee-wizard.tsx
- packages/portal/components/wizard-steps/step-role.tsx
- packages/portal/components/wizard-steps/step-persona.tsx
- packages/portal/components/wizard-steps/step-tools.tsx
- packages/portal/components/wizard-steps/step-channels.tsx
- packages/portal/components/wizard-steps/step-escalation.tsx
- packages/portal/components/wizard-steps/step-review.tsx
- packages/portal/components/template-gallery.tsx
- packages/portal/lib/queries.ts
- packages/portal/lib/api.ts
- packages/portal/lib/system-prompt-builder.ts
autonomous: true
requirements: [EMPL-01, EMPL-02, EMPL-03, EMPL-04, EMPL-05]
must_haves:
truths:
- "New Employee button presents three options: Templates, Guided Setup, Advanced"
- "Template gallery shows card grid with name, role, tools — one-click deploy creates agent"
- "Wizard walks through 5 steps: Role, Persona, Tools, Channels, Escalation + Review"
- "Wizard auto-generates system prompt (hidden from user) with AI transparency clause"
- "Advanced option opens existing Agent Designer with full control"
- "Wizard-created and template-deployed agents appear in Agent Designer for editing"
- "Only platform_admin and customer_admin can access creation paths"
artifacts:
- path: "packages/portal/app/(dashboard)/agents/new/page.tsx"
provides: "Three-option entry screen (Templates / Guided Setup / Advanced)"
- path: "packages/portal/app/(dashboard)/agents/new/templates/page.tsx"
provides: "Template gallery page"
- path: "packages/portal/app/(dashboard)/agents/new/wizard/page.tsx"
provides: "5-step wizard page"
- path: "packages/portal/app/(dashboard)/agents/new/advanced/page.tsx"
provides: "Advanced mode (AgentDesigner)"
- path: "packages/portal/components/employee-wizard.tsx"
provides: "Wizard client component with state management and stepper"
- path: "packages/portal/components/template-gallery.tsx"
provides: "Template card grid with preview and one-click deploy"
- path: "packages/portal/lib/system-prompt-builder.ts"
provides: "TypeScript system prompt builder (mirrors Python version)"
- path: "packages/portal/lib/queries.ts"
provides: "useTemplates and useDeployTemplate hooks"
- path: "packages/portal/lib/api.ts"
provides: "Template and TemplateDeployResponse types"
key_links:
- from: "packages/portal/app/(dashboard)/agents/new/page.tsx"
to: "/agents/new/templates, /agents/new/wizard, /agents/new/advanced"
via: "router.push on card click"
pattern: "router\\.push.*agents/new/(templates|wizard|advanced)"
- from: "packages/portal/components/template-gallery.tsx"
to: "/api/portal/templates"
via: "useTemplates TanStack Query hook"
pattern: "useTemplates"
- from: "packages/portal/components/template-gallery.tsx"
to: "/api/portal/templates/{id}/deploy"
via: "useDeployTemplate mutation"
pattern: "useDeployTemplate"
- from: "packages/portal/components/employee-wizard.tsx"
to: "/api/portal/tenants/{tid}/agents"
via: "useCreateAgent mutation with auto-generated system prompt"
pattern: "useCreateAgent|buildSystemPrompt"
- from: "packages/portal/lib/system-prompt-builder.ts"
to: "packages/portal/components/employee-wizard.tsx"
via: "import in review step"
pattern: "buildSystemPrompt"
---
<objective>
Complete frontend for all three employee creation paths: three-option entry screen, template gallery with one-click deploy, 5-step guided wizard, and Advanced mode (existing Agent Designer relocated).
Purpose: Give operators and customer admins three ways to create AI employees — from fastest (templates) to most control (Advanced) — all producing agents editable in Agent Designer.
Output: New pages (entry, templates, wizard, advanced), wizard step components, template gallery component, system prompt builder, TanStack Query hooks for templates API.
</objective>
<execution_context>
@/home/adelorenzo/.claude/get-shit-done/workflows/execute-plan.md
@/home/adelorenzo/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/05-employee-design/05-CONTEXT.md
@.planning/phases/05-employee-design/05-RESEARCH.md
@.planning/phases/05-employee-design/05-VALIDATION.md
@.planning/phases/05-employee-design/05-01-SUMMARY.md
<interfaces>
<!-- Key types and contracts from Plan 01 that this plan depends on -->
From packages/shared/shared/api/templates.py (created in Plan 01):
```python
# GET /api/portal/templates -> list[TemplateResponse]
# GET /api/portal/templates/{id} -> TemplateResponse
# POST /api/portal/templates/{id}/deploy -> TemplateDeployResponse
class TemplateResponse(BaseModel):
id: str
name: str
role: str
description: str
category: str
persona: str
system_prompt: str
model_preference: str
tool_assignments: list[str]
escalation_rules: list[dict]
is_active: bool
sort_order: int
created_at: datetime
class TemplateDeployRequest(BaseModel):
tenant_id: str
class TemplateDeployResponse(BaseModel):
agent: AgentResponse # the newly created agent
```
From packages/portal/lib/api.ts (existing):
```typescript
export interface Agent { id: string; tenant_id: string; name: string; role: string; persona: string; system_prompt: string; model_preference: string; tool_assignments: string[]; escalation_rules: EscalationRule[]; is_active: boolean; ... }
export interface AgentCreate { name: string; role: string; persona?: string; system_prompt?: string; model_preference?: string; tool_assignments?: string[]; escalation_rules?: EscalationRule[]; is_active?: boolean; }
export interface EscalationRule { condition: string; action: string; }
```
From packages/portal/lib/queries.ts (existing):
```typescript
export function useCreateAgent(): UseMutationResult<Agent, Error, { tenantId: string; data: AgentCreate }>
export function useChannelConnections(tenantId: string): UseQueryResult<ChannelConnection[]>
export const queryKeys = { agents: (tenantId: string) => [...], ... }
```
From packages/portal/components/agent-designer.tsx (existing):
```typescript
export interface AgentDesignerValues { tenant_id: string; name: string; role: string; persona: string; system_prompt: string; model_preference: string; tool_assignments: string[]; escalation_rules: EscalationRule[]; is_active: boolean; }
export function AgentDesigner(props: { defaultValues?: Partial<AgentDesignerValues>; tenants: Tenant[]; onSubmit: (data: AgentDesignerValues) => Promise<void>; isLoading: boolean; submitLabel: string; })
```
From packages/portal/components/onboarding-stepper.tsx (existing pattern to adapt):
```typescript
export interface StepInfo { number: number; label: string; description: string; }
export function OnboardingStepper({ currentStep }: { currentStep: number })
```
</interfaces>
</context>
<tasks>
<task type="auto">
<name>Task 1: Three-option entry screen, Advanced page, TypeScript types, TanStack hooks, and system prompt builder</name>
<files>
packages/portal/app/(dashboard)/agents/new/page.tsx,
packages/portal/app/(dashboard)/agents/new/advanced/page.tsx,
packages/portal/lib/api.ts,
packages/portal/lib/queries.ts,
packages/portal/lib/system-prompt-builder.ts
</files>
<action>
1. Add TypeScript types to `packages/portal/lib/api.ts`:
```typescript
export interface Template {
id: string; name: string; role: string; description: string; category: string;
persona: string; system_prompt: string; model_preference: string;
tool_assignments: string[]; escalation_rules: EscalationRule[];
is_active: boolean; sort_order: number; created_at: string;
}
export interface TemplateDeployResponse { agent: Agent; }
```
2. Add TanStack Query hooks to `packages/portal/lib/queries.ts`:
- Add query key: `templates: () => ["templates"] as const`
- `useTemplates(): UseQueryResult<Template[]>` — GET /api/portal/templates
- `useDeployTemplate(): UseMutationResult<TemplateDeployResponse, Error, { templateId: string; tenantId: string }>` — POST /api/portal/templates/{templateId}/deploy with body { tenant_id }. On success, invalidate queryKeys.agents(tenantId) and queryKeys.allAgents.
3. Create `packages/portal/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`
- Assembles: "You are {name}, {role}.\n\n{persona}" (skip persona line if empty)
- If tools not empty: "\n\nYou have access to the following tools: {tools.join(', ')}."
- If escalation rules not empty: "\n\nEscalation rules:\n" + rules mapped to "- If {condition}: {action}"
- ALWAYS append: "\n\nWhen directly asked if you are an AI, always disclose that you are an AI assistant."
- This mirrors the Python build_system_prompt from Plan 01
4. Replace `packages/portal/app/(dashboard)/agents/new/page.tsx` with three-option entry screen:
- "use client" component wrapped in Suspense (existing pattern)
- Page title: "New AI Employee" with subtitle: "Choose how you want to get started"
- Three Card components in a responsive grid (grid-cols-1 md:grid-cols-3 gap-6):
a. **Templates** — Icon: LayoutTemplate (lucide-react). Headline: "Templates". Subtitle: "Deploy in 30 seconds". Description: "Choose from pre-built AI employees with sensible defaults. One click to deploy." Button: "Browse Templates" -> router.push(`/agents/new/templates?tenant=${tenantId}`)
b. **Guided Setup** — Icon: Wand2 (lucide-react). Headline: "Guided Setup". Subtitle: "5-minute setup". Description: "Step-by-step wizard walks you through role, persona, tools, channels, and escalation." Button: "Start Setup" -> router.push(`/agents/new/wizard?tenant=${tenantId}&step=1`)
c. **Advanced** — Icon: Settings (lucide-react). Headline: "Advanced". Subtitle: "Full control". Description: "Complete Agent Designer with manual system prompt editing and all configuration options." Button: "Open Designer" -> router.push(`/agents/new/advanced?tenant=${tenantId}`)
- Include tenant selector dropdown at top if the user has multiple tenants (reuse useTenants pattern from current page)
- Include "Back to Employees" button with ArrowLeft icon (existing pattern)
- Template card should have a visual emphasis (e.g., "Recommended" badge or primary border) since it is the fastest path per user decision
5. Create `packages/portal/app/(dashboard)/agents/new/advanced/page.tsx`:
- Move the existing NewAgentContent logic from the current page.tsx here
- "use client", Suspense wrapper, useTenants, useCreateAgent, AgentDesigner component
- Identical functionality to the old /agents/new page — this IS the existing Agent Designer in create mode
- Page title: "Advanced Agent Designer" with "Back to New Employee" link to /agents/new
</action>
<verify>
<automated>cd /home/adelorenzo/repos/konstruct/packages/portal && npx next build 2>&1 | tail -20</automated>
</verify>
<done>Three-option entry screen at /agents/new shows Templates, Guided Setup, and Advanced cards. Advanced page at /agents/new/advanced renders existing AgentDesigner. Template types and hooks added to api.ts and queries.ts. System prompt builder function created. Portal builds without errors.</done>
</task>
<task type="auto">
<name>Task 2: Template gallery page and wizard with all step components</name>
<files>
packages/portal/app/(dashboard)/agents/new/templates/page.tsx,
packages/portal/app/(dashboard)/agents/new/wizard/page.tsx,
packages/portal/components/template-gallery.tsx,
packages/portal/components/employee-wizard.tsx,
packages/portal/components/wizard-steps/step-role.tsx,
packages/portal/components/wizard-steps/step-persona.tsx,
packages/portal/components/wizard-steps/step-tools.tsx,
packages/portal/components/wizard-steps/step-channels.tsx,
packages/portal/components/wizard-steps/step-escalation.tsx,
packages/portal/components/wizard-steps/step-review.tsx
</files>
<action>
1. Create `packages/portal/components/template-gallery.tsx` ("use client"):
- Props: `{ tenantId: string; onDeployed?: (agent: Agent) => void }`
- Uses `useTemplates()` hook to fetch templates
- Uses `useDeployTemplate()` mutation for one-click deploy
- Card grid layout: `grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4`
- Each card shows: name (bold), role, description (text-muted-foreground), tool_assignments as Badge chips, category as small label
- Each card has two actions: "Preview" button (expands to show full config: persona, escalation rules, system_prompt) and "Deploy" button (primary, calls deploy mutation)
- Preview: use a collapsible section or Dialog. Show persona paragraph, escalation rules list, model preference. Per user decision: "Preview expands to show full configuration before deploying"
- Deploy button: on click, call `deployTemplate.mutateAsync({ templateId: template.id, tenantId })`, on success redirect to `/agents/${newAgent.id}?tenant=${tenantId}` (satisfies EMPL-05: agent appears in Agent Designer for customization)
- Loading state: skeleton cards while fetching
- Deploy loading state: button shows spinner/disabled during mutation
- Error state: toast or inline error on deploy failure
- Empty state: "No templates available" (should not happen with seed data, but handle gracefully)
2. Create `packages/portal/app/(dashboard)/agents/new/templates/page.tsx`:
- "use client" with Suspense wrapper
- Reads `tenant` from `useSearchParams()`
- Renders: Back button to /agents/new, page title "Template Library" with subtitle "Choose a pre-built AI employee to deploy instantly", then `<TemplateGallery tenantId={tenantId} />`
- If no tenant selected, show tenant selector (same pattern as entry screen)
3. Create `packages/portal/components/employee-wizard.tsx` ("use client"):
- Props: `{ tenantId: string; initialStep?: number }`
- State: `useState<Partial<WizardData>>({})` where WizardData = `{ name: string; roleTitle: string; persona: string; tool_assignments: string[]; channel_ids: string[]; escalation_rules: { condition: string; action: string }[] }`
- State: `currentStep` number (1-6, where 6 is the review step)
- Renders a stepper component adapted from OnboardingStepper pattern with 5 numbered steps + review:
Steps: Role (1), Persona (2), Tools (3), Channels (4), Escalation (5), Review (6)
Stepper shows steps 1-5 as numbered circles with labels; step 6 (Review) is a distinct "Review & Deploy" section
- Each step renders the corresponding step component, passing wizardData and onNext callback
- onNext: `(updates: Partial<WizardData>) => { setWizardData(prev => ({...prev, ...updates})); setCurrentStep(prev => prev + 1); }`
- onBack: `setCurrentStep(prev => prev - 1)` (show "Back" button on steps 2-6)
- URL update on step change: `router.replace(`/agents/new/wizard?tenant=${tenantId}&step=${currentStep}`, { scroll: false })`
- Note: wizard state is in React state, NOT URL params (per research: persona text would pollute URL). State loss on refresh is acceptable per research pitfall analysis.
4. Create step components in `packages/portal/components/wizard-steps/`:
a. `step-role.tsx` — "What role will they fill?"
- Props: `{ data: Partial<WizardData>; onNext: (updates: Partial<WizardData>) => void }`
- Two inputs: Employee Name (text input, required, placeholder "e.g., Mara") and Job Title / Role (text input, required, placeholder "e.g., Customer Support Representative")
- Use react-hook-form with zod schema: `z.object({ name: z.string().min(1, "Name is required").max(255), roleTitle: z.string().min(1, "Role is required").max(255) })`
- Use standardSchemaResolver (NOT zodResolver — project decision)
- "Next" button calls onNext with { name, roleTitle }
- Pre-fill from data if user navigates back
b. `step-persona.tsx` — "How should they behave?"
- Textarea for persona description, placeholder: "Describe your employee's personality and communication style. For example: Professional, empathetic, solution-oriented. Fluent in English and Spanish. Prefers concise responses."
- Optional field — can proceed with empty (will generate a default persona in system prompt)
- "Next" button calls onNext with { persona }
c. `step-tools.tsx` — "What tools can they use?"
- Multi-select for tool_assignments using Badge chip pattern (same as Agent Designer)
- Available tools: knowledge_base_search, zendesk_ticket_create, zendesk_ticket_lookup, calendar_book (hardcoded list — same as Agent Designer)
- Click to toggle, selected tools shown as highlighted Badges
- Optional — can proceed with no tools selected
- "Next" button calls onNext with { tool_assignments }
d. `step-channels.tsx` — "Where will they work?"
- Uses `useChannelConnections(tenantId)` to fetch connected channels
- If channels exist: show checkboxes for each channel (channel_type + workspace_id), pre-select all
- If NO channels connected: show info message "No channels connected yet. Your employee will be deployed and can be assigned to channels later." (per research pitfall 4) — allow proceeding with empty selection
- "Next" button calls onNext with { channel_ids: selectedChannelIds }
- NOTE: In v1, agent routing is tenant-scoped not per-agent. The channel step is informational — selecting channels does NOT create a channel-agent join. The agent will respond in all tenant channels. Display this as "Your employee will be active in all connected channels" with the channel list shown for confirmation.
e. `step-escalation.tsx` — "When should they hand off to a human?"
- Dynamic list of escalation rules (add/remove)
- Each rule: condition input (text, placeholder "e.g., billing_dispute AND attempts > 2") + action select (handoff_human, notify_admin)
- "Add Rule" button to add another row
- Optional — can proceed with no rules
- "Next" button calls onNext with { escalation_rules }
f. `step-review.tsx` — "Review & Deploy"
- Props: `{ data: WizardData; tenantId: string; onBack: () => void }`
- Summary card showing all configured values: Name, Role, Persona (or "Default"), Tools (or "None"), Channels ("All connected" or specific list), Escalation Rules (or "None")
- Does NOT show the system prompt (per locked decision: system prompt hidden from wizard user)
- "Deploy Employee" primary button — calls useCreateAgent mutation with:
```
{ tenantId, data: { name: data.name, role: data.roleTitle, persona: data.persona || "", system_prompt: buildSystemPrompt({ name: data.name, role: data.roleTitle, persona: data.persona, tool_assignments: data.tool_assignments, escalation_rules: data.escalation_rules }), model_preference: "quality", tool_assignments: data.tool_assignments || [], escalation_rules: data.escalation_rules || [], is_active: true } }
```
- On success: redirect to `/agents/${newAgent.id}?tenant=${tenantId}` (agent edit page — satisfies EMPL-05)
- Error handling: show inline error message on failure
5. Create `packages/portal/app/(dashboard)/agents/new/wizard/page.tsx`:
- "use client" with Suspense wrapper
- Reads `tenant` and `step` from `useSearchParams()`
- Renders: Back button to /agents/new, page title "Create AI Employee" with subtitle "Step-by-step guided setup", then `<EmployeeWizard tenantId={tenantId} initialStep={parseInt(step || "1")} />`
- If no tenant selected, show tenant selector
</action>
<verify>
<automated>cd /home/adelorenzo/repos/konstruct/packages/portal && npx next build 2>&1 | tail -20</automated>
</verify>
<done>Template gallery at /agents/new/templates shows card grid of templates with preview and one-click deploy. Wizard at /agents/new/wizard walks through 5 steps (Role, Persona, Tools, Channels, Escalation) plus Review with Deploy button. Both paths create agents via existing API. All pages build without TypeScript errors. Agents created via either path redirect to edit page (EMPL-05).</done>
</task>
</tasks>
<verification>
1. `cd packages/portal && npx next build` succeeds without errors
2. All three paths from /agents/new route correctly: Templates, Guided Setup, Advanced
3. Template gallery loads templates from API and deploys with one click
4. Wizard collects data across 5 steps and creates agent with auto-generated system prompt
5. `pytest tests/ -x` still passes (no backend regressions)
</verification>
<success_criteria>
- Three-option entry screen at /agents/new with Templates (recommended), Guided Setup, and Advanced cards
- Template gallery shows 7 templates as cards with preview and one-click deploy
- Template deploy creates agent and redirects to edit page
- 5-step wizard collects Role, Persona, Tools, Channels, Escalation then shows Review summary
- Wizard Deploy creates agent with auto-generated system prompt (including AI transparency clause) and redirects to edit page
- Advanced mode renders existing AgentDesigner unchanged
- Portal builds without TypeScript errors
</success_criteria>
<output>
After completion, create `.planning/phases/05-employee-design/05-02-SUMMARY.md`
</output>

View File

@@ -0,0 +1,97 @@
---
phase: 05-employee-design
plan: 03
type: execute
wave: 3
depends_on: ["05-02"]
files_modified: []
autonomous: false
requirements: [EMPL-01, EMPL-02, EMPL-03, EMPL-04, EMPL-05]
must_haves:
truths:
- "All three creation paths work end-to-end"
- "Template-deployed agents respond in channels"
- "Wizard-created agents are editable in Agent Designer"
artifacts: []
key_links: []
---
<objective>
Human verification of all three employee creation paths: template deployment, guided wizard, and Advanced Agent Designer.
Purpose: Confirm that the complete Employee Design feature works visually and functionally before marking Phase 5 complete.
Output: Human approval or issue list for gap closure.
</objective>
<execution_context>
@/home/adelorenzo/.claude/get-shit-done/workflows/execute-plan.md
@/home/adelorenzo/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/phases/05-employee-design/05-CONTEXT.md
@.planning/phases/05-employee-design/05-01-SUMMARY.md
@.planning/phases/05-employee-design/05-02-SUMMARY.md
</context>
<tasks>
<task type="checkpoint:human-verify" gate="blocking">
<name>Task 1: Verify all three employee creation paths</name>
<files></files>
<action>
Human verifies the complete three-path AI employee creation system: template gallery with one-click deploy, 5-step guided wizard, and Advanced Agent Designer mode.
Prerequisites: Docker Compose stack running (docker compose up -d), portal at http://localhost:3000, logged in as platform_admin or customer_admin.
1. **Entry Screen**: Navigate to Employees list, click "New Employee" (or go to /agents/new)
- Verify three cards appear: Templates (recommended), Guided Setup, Advanced
- Verify Templates card has emphasis/recommended styling
2. **Template Path**: Click "Browse Templates"
- Verify 7 template cards appear in a grid (Customer Support Rep, Sales Assistant, Office Manager, Project Coordinator, Financial Manager, Controller, Accountant)
- Click "Preview" on Customer Support Rep — verify it shows persona, tools, escalation rules
- Click "Deploy" on any template — verify agent is created and you are redirected to the agent edit page
- Verify the deployed agent appears in the Employees list with is_active=true
3. **Wizard Path**: Go back to /agents/new, click "Start Setup"
- Step 1 (Role): Enter name and role title, click Next
- Step 2 (Persona): Enter a persona description, click Next
- Step 3 (Tools): Select one or more tools, click Next
- Step 4 (Channels): Verify connected channels are shown (or empty state message), click Next
- Step 5 (Escalation): Add an escalation rule, click Next
- Review: Verify all entered data is displayed in summary, system prompt is NOT shown
- Click "Deploy Employee" — verify agent is created and redirected to edit page
- Open the agent in Agent Designer — verify system_prompt was auto-generated (contains AI transparency clause)
4. **Advanced Path**: Go back to /agents/new, click "Open Designer"
- Verify the full Agent Designer form appears (same as before Phase 5)
- All fields editable including system_prompt
5. **RBAC**: Log in as customer_operator
- Verify "New Employee" button is NOT visible or routes to 403
- Verify /agents/new entry screen is not accessible to operators
</action>
<verify>Human approval</verify>
<done>Human confirms all three creation paths work correctly, RBAC enforced, system prompt auto-generated properly</done>
</task>
</tasks>
<verification>
Human confirms all three creation paths work visually and functionally.
</verification>
<success_criteria>
- Human approves all three paths (template, wizard, advanced)
- Template deploy creates working agent
- Wizard generates correct system prompt
- RBAC prevents operator access to creation paths
</success_criteria>
<output>
After completion, create `.planning/phases/05-employee-design/05-03-SUMMARY.md`
</output>

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)

View File

@@ -0,0 +1,80 @@
---
phase: 5
slug: employee-design
status: draft
nyquist_compliant: false
wave_0_complete: false
created: 2026-03-24
---
# Phase 5 — Validation Strategy
> Per-phase validation contract for feedback sampling during execution.
---
## Test Infrastructure
| Property | Value |
|----------|-------|
| **Framework** | pytest 8.x + pytest-asyncio (existing) |
| **Config file** | `pyproject.toml` (existing) |
| **Quick run command** | `pytest tests/unit -x -q` |
| **Full suite command** | `pytest tests/ -x` |
| **Estimated runtime** | ~30 seconds |
---
## Sampling Rate
- **After every task commit:** Run `pytest tests/unit -x -q`
- **After every plan wave:** Run `pytest tests/ -x`
- **Before `/gsd:verify-work`:** Full suite must be green
- **Max feedback latency:** 30 seconds
---
## Per-Task Verification Map
| Task ID | Plan | Wave | Requirement | Test Type | Automated Command | File Exists | Status |
|---------|------|------|-------------|-----------|-------------------|-------------|--------|
| 05-xx | 01 | 1 | EMPL-01 | unit | `pytest tests/unit/test_system_prompt_builder.py -x` | ❌ W0 | ⬜ pending |
| 05-xx | 01 | 1 | EMPL-02,03 | integration | `pytest tests/integration/test_templates.py -x` | ❌ W0 | ⬜ pending |
| 05-xx | 01 | 1 | EMPL-04 | integration | `pytest tests/integration/test_templates.py::test_deploy_template_rbac -x` | ❌ W0 | ⬜ pending |
| 05-xx | 02 | 2 | EMPL-01 | build | `cd packages/portal && npx next build` | ✅ | ⬜ pending |
| 05-xx | 02 | 2 | EMPL-02 | build | `cd packages/portal && npx next build` | ✅ | ⬜ pending |
| 05-xx | 02 | 2 | EMPL-05 | build | `cd packages/portal && npx next build` | ✅ | ⬜ pending |
*Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky*
---
## Wave 0 Requirements
- [ ] `tests/unit/test_system_prompt_builder.py` — EMPL-01: system prompt auto-generation from wizard inputs
- [ ] `tests/integration/test_templates.py` — EMPL-02,03,04: template CRUD, deploy, RBAC
---
## Manual-Only Verifications
| Behavior | Requirement | Why Manual | Test Instructions |
|----------|-------------|------------|-------------------|
| Three-option entry point renders correctly | EMPL-01,02 | UI layout | Click "New Employee", verify Templates/Guided Setup/Advanced options appear |
| Wizard stepper navigates through all 5 steps | EMPL-01 | UI flow | Walk through Role → Persona → Tools → Channels → Escalation |
| Template gallery shows card grid with previews | EMPL-02 | UI visual | Browse templates, click preview, verify details expand |
| One-click template deploy creates functional agent | EMPL-03 | End-to-end | Deploy template, verify agent appears in list, sends in channels |
| Wizard-created agent editable in Agent Designer | EMPL-05 | UI flow | Create via wizard, click edit, verify Agent Designer opens with all fields |
---
## Validation Sign-Off
- [ ] All tasks have `<automated>` verify or Wave 0 dependencies
- [ ] Sampling continuity: no 3 consecutive tasks without automated verify
- [ ] Wave 0 covers all MISSING references
- [ ] No watch-mode flags
- [ ] Feedback latency < 30s
- [ ] `nyquist_compliant: true` set in frontmatter
**Approval:** pending