Project: FastAPI Appointment Bot with Vertical Slice Architecture Duration: January 14-28, 2026 - In Progress Total Time: ~43h
Production-ready FastAPI application implementing a Facebook Messenger appointment booking system. Features Info-Gatherer AI agent with LangGraph workflow, Google Calendar integration, and Langfuse observability. Built with Vertical Slice Architecture, strict type checking (MyPy + Pyright), and structured logging.
- Setup: Initialized git repo with VSA foundation
- Architecture: Vertical Slice Architecture (VSA) patterns established
- Steering Docs: AGENTS.md, product.md, tech.md, structure.md, logging-standard.md
- Standards: Ruff, MyPy, Pyright, Pytest configuration files
- Config:
app/core/config.pywith Pydantic Settings, LRU cache singleton - Logging:
app/core/logging.pywith structlog, JSON output, hybrid dotted namespace - Testing: 18 tests (7 config + 11 logging)
- Pattern:
{domain}.{component}.{action}_{state}(e.g.,application.lifecycle.started)
- Main App:
app/main.pywith lifespan context manager - Health:
/healthendpoint returning status, version, environment - Tests: 3 integration tests for health endpoint
- Middleware:
app/core/middleware.pywith RequestLoggingMiddleware - Features: UUID4 request IDs, duration tracking,
X-Request-IDheader - Logging:
request.http_received,request.http_completed,request.http_failed - Tests: 8 integration tests
- Database:
app/core/database.pywith async SQLAlchemy - Features: Connection pooling (5+10), PostgreSQL/SQLite support
- Challenge: Async generator infinite loop with
async for - Solution: Manual generator control (
__anext__()+aclose()) - Tests: 7 unit tests
- Docs: Updated testing-standard.md with async patterns
- Fixtures:
tests/conftest.pywith client, test_settings, cleanup - Challenge: Test suite hanging after completion (async engine not disposed)
- Solution: Session-scoped cleanup fixture with
asyncio.run(engine.dispose()) - Tests: 33 passing, exits cleanly
- Quality: All checks passing (Ruff, MyPy, Pyright)
- README: Comprehensive 1200+ line guide with VSA examples
- Coverage: 92% test coverage
- Status: Foundation complete, production ready
- Spec: Created
.kiro/specs/facebook-messenger-monitoring/ - Requirements: 14 requirements with EARS patterns
- Design: 12 correctness properties, VSA architecture
- Tasks: 16 implementation tasks with comprehensive testing
- Directory:
app/messenger/with exceptions, tests - Config: Supabase, Facebook tokens, rate limiting, retry settings
- Validation:
validate_messenger_settings()for startup checks - Tests: 6 configuration tests
- Models: Conversation (PSID unique), Message (message_id unique)
- Schemas: Webhook payloads, API responses, search results
- Migration:
alembic/versions/a289d8e6eb84_*with indexes - Mixin: TimestampMixin for created_at/updated_at
- Security:
app/messenger/security.pywith HMAC-SHA256 - Features: Constant-time comparison, early rejection
- Logging:
messenger.webhook.signature_*events - Tests: 7 security tests (valid, invalid, malformed, timing attack)
- Repository:
app/messenger/repository.py(240 lines) - Methods: get_or_create_conversation, save_message, search_conversations
- Challenge: SQLite BigInteger incompatibility
- Solution: Changed to Integer for autoincrement support
- Tests: 13 tests (0.46s execution)
- Service:
app/messenger/service.py(280 lines) - Features: Retry with exponential backoff (1s, 2s, 4s), error notifications
- PSID Logic: Sender for regular messages, recipient for echo messages
- Tests: 17 tests covering retry, backoff, processing
- Rate Limiter: Sliding window algorithm (100 msg/min)
- Implementation: Deque-based in-memory storage
- Middleware: Applied only to POST /webhook endpoint
- Tests: 8 rate limiting tests
- Endpoint:
GET /webhook/messages/replies/{message_id} - Repository:
get_message_replies()method - Tests: 4 tests validating reply chain queries
- Schemas:
app/info_gatherer/schemas.py(6 models) - Models: AppointmentInfo, ValidationResult, ResponseGeneration, WorkflowState
- Validation: Phone (10+ digits), future datetime, confidence range
- Tests: 25 schema validation tests
- State:
app/info_gatherer/graph/state.pywith InfoGathererState - Fields: Input (psid, query), extracted info, workflow control, agent outputs
- Design: TypedDict with total=False for incremental updates
- Issue: Tests connecting to production PostgreSQL
- Solution: DATABASE_TEST_URL with separate test fixtures
- Pydantic AI: Updated model names to
openai:gpt-4o-miniformat - Tests: 207 passing, 0 warnings
- Routing:
app/info_gatherer/graph/routing.py(3 routing functions) - Functions: route_after_extraction, route_after_validation, route_after_confirmation
- Safety: MAX_ITERATIONS = 10 to prevent infinite loops
- Tests: 401 lines covering all routing paths
- Infrastructure:
app/info_gatherer/error_handling.py(652 lines) - Features: Exponential backoff, 30s LLM timeout, 10s DB timeout
- Fallbacks: 10 different error type responses
- PII Protection: Phone numbers masked in logs
- Tests: 32 error handling tests
- Fixed: 23 ANN401 errors with file-level suppressions
- Updated: Deprecated asyncio.iscoroutinefunction → inspect.iscoroutinefunction
- Agent Fixes: Added empty
deps={}to Pydantic AI agent.run() calls - Status: Ruff clean, MyPy clean
- Workflow:
app/info_gatherer/graph/workflow.py(264 lines) - Features: StateGraph with PostgreSQL checkpointer
- Functions: create_workflow, create_workflow_with_checkpointer
- Dependencies: langgraph>=0.2.0, langgraph-checkpoint-postgres>=2.0.0
- Tests: 542 lines of workflow tests
- Orchestrator:
app/info_gatherer/orchestrator.py(493 lines) - Features: Session lifecycle (start/process/end), activation (@appointment), deactivation (@cancel, 12h timeout)
- Tests: 18 orchestrator tests with mock dependencies
- Client:
app/messenger/facebook_client.py(264 lines) - Features: Rate limiting (100/min), exponential backoff retry
- Integration: Webhook → Orchestrator → Facebook API pipeline
- Tests: 8 end-to-end webhook integration tests
- Quality: 362 tests passing, all quality gates green
- Client:
app/info_gatherer/calendar_client.py(280 lines) - Auth: Service account with JSON credentials (file or base64)
- Features: Event creation, graceful fallback on failure
- Type Stubs: Custom stubs for google-api-python-client
- Docs: README updated with Google Cloud Console setup guide
- Agent:
ConfirmationAssessmentfor natural language understanding - Features: Handles "yes", "yep", "looks good", cultural variations
- Context: Full conversation history scanning on first activation
- Status Management: Explicit preservation during confirmation flow
- Availability:
check_availability()using freebusy.query API - Alternatives: 4 slot suggestions (earlier, later, next day, next week)
- Business Hours: 9AM-6PM enforcement for all suggestions
- Lifecycle: pending → complete/reschedule status transitions
- Migration: Partial unique index for active appointments only
- Resilience:
_execute_with_retry()for transient network errors
Key Decisions:
- Availability-first pattern: Check calendar before booking
- Pending-first creation: Preserve intent even if calendar check fails
- State preservation: appointment_id carried across reschedule cycles
- Simplification: Global
Agent.instrument_all()replacing per-agent setup - Removed: 150+ lines of redundant Langfuse provider logic
- Timezone: Fixed freebusy API with Bangkok timezone (UTC+7)
- Routing: Added conflict handling with
has_conflictstate flag - Dependency: Added
langfuse>=3.12.0
- Spec Docs: Created
.agents/spec/admin-dashboard/with full documentation - Requirements: 8 functional requirements + non-functional requirements
- Design: Full technical design with API specs, architecture, schemas
- Tasks: 44 implementation tasks organized in phases
- Prototype: HTML mockup (
dashboard-prototype.html) for UI reference
- Config: Added
ADMIN_PASSWORDandADMIN_JWT_SECRETto settings - Exceptions: Created exception hierarchy (AuthenticationError, ConversationNotFoundError, ExportError)
- Validation:
validate_admin_dashboard_settings()for startup checks - Tests: 8 configuration tests
- Auth:
app/admin_dashboard/auth.py(168 lines) - Features: TokenPayload model, timing-attack-safe password verification
- Functions:
verify_password(),create_access_token()(24h HS256),decode_token() - Tests: 16 auth tests covering edge cases
- Dependency:
require_admin_auth()for route protection - Schemas:
app/admin_dashboard/schemas.pywith Pydantic models - Models: LoginRequest, LoginResponse, ConversationListQuery, EvalCaseExportRequest
- Tests: 7 dependency tests, 24 schema tests
- Repository:
app/admin_dashboard/repository.py(521 lines) - Queries: get_conversations_with_status, get_conversation_detail, get_messages_for_conversation
- Features: Paginated queries with LEFT JOINs, search/status filters
- Data Classes: ConversationListItem, ConversationDetail, MessageData, ConversationStatsData
- Tests: 15 integration tests
- Service:
app/admin_dashboard/service.py(417 lines) - Methods: list_conversations, get_conversation_detail, get_conversation_messages, generate_eval_case
- Helpers: Avatar color from PSID hash (MD5), initials extraction, text truncation
- Tests: 26 tests covering all methods
- SSE:
app/admin_dashboard/sse.py(293 lines) - Streams: conversation_list_stream (2s polling), message_stream
- Events: conversation_created, conversation_updated, message_created, workflow_updated, heartbeat
- Pattern: Polling approach for database portability
- Tests: 12 tests for streaming behavior
- Routes:
app/admin_dashboard/routes.py(350 lines) - Auth Endpoints: POST /api/admin/auth/login, /logout
- Conversation Endpoints: GET /api/admin/conversations, /{psid}, /{psid}/messages
- Export Endpoint: POST /api/admin/export/eval-case
- SSE Endpoints: GET /api/admin/sse/conversations, /messages/{psid}
- Tests: 18 route tests
- Tests:
app/admin_dashboard/tests/test_integration.py(569 lines) - Coverage: 11 integration tests covering full flows
- Flows: Login → conversations → messages, Login → select → export
- Validation: SSE auth requirements, database error handling
- Status: All backend tests passing (135 admin-dashboard tests)
- Setup:
frontend/with Vite 7.2.4, React 19.2.0, TypeScript 5.9.3 - Styling: Tailwind CSS 4.1.18 with Vite plugin
- TypeScript: Strict mode with all additional checks enabled
- Structure: Path aliases (@/, @/api, @/components, @/contexts, @/hooks, @/pages, @/types)
- Proxy: Dev server configured for API proxy to localhost:8000
- Types:
frontend/src/types/mirroring backend schemas - API Client:
frontend/src/api/client.tswith fetch wrapper - Auth: Bearer token handling, auto-refresh on 401
- Methods: login, logout, fetchConversations, fetchMessages, exportEvalCase
- SSE Client:
frontend/src/api/sse.ts - Features: Exponential backoff reconnection, auto-reconnect on failure
- Events: onConversationCreated, onConversationUpdated, onMessageCreated
- AuthContext:
frontend/src/contexts/AuthContext.tsx - Features: Token management, auto-logout on expiry, localStorage persistence
- SelectionContext:
frontend/src/contexts/SelectionContext.tsx - Features: Message selection with 20-message limit for eval case export
- Layout:
frontend/src/components/Layout/Layout.tsx- Three-panel responsive design - Header:
frontend/src/components/Header/Header.tsx- Live indicator, logout button - Pattern: Resizable panels for conversation list, chat view, eval panel
- ConversationList:
frontend/src/components/ConversationList/ConversationList.tsx - Features: Search input, status filters, pagination
- UI: Avatar with initials, message preview, timestamp, status badge
- ChatView:
frontend/src/components/ChatView/ChatView.tsx - Features: Message list, selection checkboxes, auto-scroll
- UI: User/bot message differentiation, attachment preview, timestamp
- EvalPanel:
frontend/src/components/EvalPanel/EvalPanel.tsx - Features: JSON preview, export button, copy to clipboard
- Options: Include context, include workflow state, agent type selection
- LoginPage: Password authentication UI with error handling
- DashboardPage: Three-panel composition with SSE real-time updates
- Hooks: useConversations, useMessages, useConversationSSE, useMessageSSE
- Routing: Auth-based routing (LoginPage ↔ DashboardPage)
- Repository: Fixed AsyncGenerator type hints in
admin_dashboard/repository.py - Orchestrator: Updated
info_gatherer/orchestrator.pywith proper typing - Tests: Added
monkeypatchfixtures for environment isolation - Docs: Updated DEVLOG.md and README.md with current progress
- Kiro: Added spec workflow prompts (requirements, design, tasks, execute)
- Files: 14 files changed, 3200+ lines added
- Dockerfile: Multi-stage build with uv package manager
- Docker Compose: App + PostgreSQL services with health checks
- Frontend: Separate Dockerfile for Vite build
- Files:
Dockerfile,docker-compose.yml,frontend/Dockerfile,.dockerignore - Docs: README updated with Docker deployment guide
- Workflow:
.github/workflows/ci.ymlwith test, lint, type-check jobs - Matrix: Initially Ubuntu + Windows, later simplified to Ubuntu-only
- Services: PostgreSQL service container for integration tests
- Caching: uv cache for faster dependency installation
- Issue: react-refresh and synchronous setState warnings
- Solution: Extracted context hooks to separate files
- Pattern:
authContext.ts,selectionContext.ts,selectionHooks.ts - JsonPreview: Refactored for proper React patterns
- Files: 11 files changed, context separation
- Problem: Tests failing in CI due to missing external service mocks
- Solution: Root
conftest.pywithautouse=Truefixture - Fixture:
isolate_external_servicesmocks OPENAI_API_KEY, LANGFUSE_, GOOGLE_ - CI Script:
scripts/test_ci_simulation.pyfor local CI parity testing - Docs: Added
pytest-standard.mdexplaining conftest hierarchy - Pre-commit: Added
.pre-commit-config.yamlfor automated checks - Decision: Ubuntu-only CI (matches Docker deployment target)
- VSA: Feature-first organization for maintainability and AI-friendliness
- Type Safety: Dual-layer (MyPy + Pyright strict) catches more issues
- Logging: Hybrid dotted namespace for AI/LLM parseability
- Admin Dashboard: Read-only access to existing data, JWT auth, SSE for real-time updates
- Async Generators: Manual control (
__anext__+aclose) for dependency generators - Test Cleanup: Session-scoped fixtures for async resource disposal
- Calendar Integration: Service account auth, graceful degradation on failure
- PostgreSQL-specific: Partial unique index not SQLite compatible
- LangGraph types: Accept Pyright warnings (incomplete type stubs)
- ANN401 suppressions: Justified for generic function wrappers
- SSE Polling: 2s database polling vs LISTEN/NOTIFY for portability
- Admin Auth: Simple password auth (single admin) vs full user management
- Ubuntu-only CI: Matches Docker deployment target (no Windows CI needed)
- Root conftest.py: Global fixture discovery for all test directories
| Day | Hours | Key Deliverables |
|---|---|---|
| Day 1 (Jan 14) | 8h | Core infrastructure, database, middleware |
| Day 2 (Jan 15) | 8h | Messenger feature (webhook, security, service) |
| Day 3 (Jan 16) | 4h | Info-Gatherer schemas, LangGraph state |
| Day 4 (Jan 17) | 1.5h | Workflow routing |
| Day 5 (Jan 18) | 9h | Error handling, orchestrator, integration |
| Day 6 (Jan 19) | 3h | Calendar, LLM confirmation |
| Day 7 (Jan 20) | 5h | Availability checking, Langfuse refactor |
| Day 8 (Jan 24) | 6h | Admin Dashboard backend (auth, repository, service, routes, SSE) |
| Day 9 (Jan 25) | 6h | Admin Dashboard frontend (Vite, React, TypeScript, components, hooks) |
| Day 10 (Jan 27) | 2h | Type warnings fixes, test fixtures, documentation |
| Day 11 (Jan 28) | 4h | Docker setup, CI/CD workflow, test infrastructure |
- Tests: 473 passing (135 admin-dashboard tests)
- Coverage: 92%
- Type Safety: MyPy + Pyright strict mode passing
- Code Quality: Zero Ruff violations
- Frontend: Vite + React 19 + TypeScript strict mode
- CI/CD: GitHub Actions with PostgreSQL service container
- Docker: Multi-stage builds for app and frontend
- Admin Dashboard polish: Error boundaries, loading states, responsive design
- Production deployment: Deploy to Render/Fly.io using Docker image
- End-to-end testing with real Facebook/Calendar APIs
- Pre-commit hooks: Enable and validate in CI