Files
konstruct/.planning/phases/05-employee-design/05-01-PLAN.md

14 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
05-employee-design 01 execute 1
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
true
EMPL-02
EMPL-03
EMPL-04
truths artifacts key_links
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
path provides contains
packages/shared/shared/models/tenant.py AgentTemplate ORM model class AgentTemplate
path provides exports
packages/shared/shared/api/templates.py Template list + deploy endpoints
templates_router
path provides exports
packages/shared/shared/prompts/system_prompt_builder.py System prompt auto-generation from wizard inputs
build_system_prompt
path provides contains
migrations/versions/007_agent_templates.py agent_templates table with 7 seed templates agent_templates
path provides
tests/unit/test_system_prompt_builder.py Unit tests for prompt builder
path provides
tests/integration/test_templates.py Integration tests for template API
from to via pattern
packages/shared/shared/api/templates.py packages/shared/shared/models/tenant.py AgentTemplate ORM model import from shared.models.tenant import.*AgentTemplate
from to via pattern
packages/shared/shared/api/templates.py packages/shared/shared/models/tenant.py Agent ORM model for deploy snapshot Agent(
from to via pattern
packages/gateway/main.py packages/shared/shared/api/templates.py Router mount templates_router
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.

<execution_context> @/home/adelorenzo/.claude/get-shit-done/workflows/execute-plan.md @/home/adelorenzo/.claude/get-shit-done/templates/summary.md </execution_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

From packages/shared/shared/models/tenant.py:

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:

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:

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):

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)

Task 1: AgentTemplate model, migration 007, system prompt builder, and tests 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 - 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 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
cd /home/adelorenzo/repos/konstruct && python -m pytest tests/unit/test_system_prompt_builder.py -x -v 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 Task 2: Template API endpoints (list, detail, deploy) with RBAC and integration tests packages/shared/shared/api/templates.py, packages/gateway/main.py, tests/integration/test_templates.py 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)
cd /home/adelorenzo/repos/konstruct && python -m pytest tests/integration/test_templates.py -x -v 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 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)

<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>
After completion, create `.planning/phases/05-employee-design/05-01-SUMMARY.md`