docs(05-employee-design): create phase plan — 3 plans in 3 waves
This commit is contained in:
257
.planning/phases/05-employee-design/05-01-PLAN.md
Normal file
257
.planning/phases/05-employee-design/05-01-PLAN.md
Normal 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>
|
||||
Reference in New Issue
Block a user