POC Scope & Achievements
What Was the POC
The Proof of Concept was a focused development phase that validated the technical feasibility of building a Slack-based knowledge management system with Notion integration and AI-powered summaries.
The POC proved:
- Slack Socket Mode integration provides reliable real-time event handling
- Symfony backend can orchestrate business logic with clean layered architecture
- AI integration (OpenAI/OpenRouter) can generate useful Dutch-language summaries
- The full Input → Process → Output loop works end-to-end
What the POC Delivered
The POC was implemented in 3 steps that each built on the previous:
Step 1 — Input: Create Lists & Add Knowledge
What was built:
- Full Orchestrator Pipeline: Controller → Orchestrator → PersistenceService → Repository
- 5 base infrastructure classes (BaseService, BaseDomainService, BaseOrchestrator, BaseMapper, BaseQueryService)
- Category and Knowledge entities with Doctrine ORM
- Slack Bolt app with App Home, Add List modal, Add Knowledge modal, message shortcut
API routes: 4 (POST/GET /api/categories, POST/GET /api/knowledge)
Step 2 — Process: Backend Content Extraction
What was built:
- ContentExtractionService with SSRF protection
- HtmlContentExtractor with Readability-based parsing
- Automatic URL content appending to knowledge items
- Graceful failure handling (never blocks saves)
New files: 3 (+ 1 modified orchestrator)
Step 3 — Output: AI-Enhanced Digest & DM Delivery
What was built:
- Full AI provider stack: AiProviderInterface → OpenAIService
- DigestOrchestrator with per-item AI summary generation
- Block Kit DM delivery via Slack chat.postMessage
- Mock mode for testing without API calls
New files: 12 (9 backend, 3 Slack)
How the POC Evolved into the MVP
The POC directly evolved into the current system. Key additions made during the MVP phase:
| Feature Added | POC Baseline | MVP Implementation |
|---|---|---|
| Digest Persistence | Ephemeral DTO returned to Slack | Full Digest entity with database persistence |
| Distribution | Slack handler built and sent DM directly | Backend DigestDistributionService with per-user delivery tracking |
| Subscriptions | Not implemented | Subscription entity with subscribe/unsubscribe/soft-delete |
| Category Settings | Basic name/description/icon | Digest frequency, day, time, enabled toggle |
| Scheduled Digests | Manual only | DigestSchedulerService with cron support |
| Notion Sync | Outbound only (Slack → Notion) | Bidirectional for all 4 entity types |
| AI Highlight Storage | Not persisted | Knowledge.highlight field with lazy generation |
| Delivery Tracking | Not implemented | DigestDelivery entity with retry support |
Technical Stack
| Layer | Technology | Purpose |
|---|---|---|
| Backend | PHP 8.2 + Symfony 7.3 | API, business logic, AI integration |
| Database | SQLite + Doctrine ORM | Local persistence |
| AI | OpenAI / OpenRouter | Dutch-language summaries |
| Bot Framework | @slack/bolt v3.17.1 + TypeScript | Slack event handling |
| External | Notion API v2022-06-28 | Secondary storage and sync |
Architecture Delivered
Entities: 5
Category ──OneToMany──→ Knowledge
Category ──OneToMany──→ Digest
Category ──OneToMany──→ Subscription (CASCADE)
Digest ──ManyToMany──→ Knowledge (via join table)
Digest ──OneToMany──→ DigestDelivery (CASCADE)Services: 38 files across 12 directories
Service/
├── Ai/ (2) — Provider interface + OpenAI/OpenRouter
├── Content/ (2) — Content extraction + HTML parser
├── Digest/ (10) — Full digest pipeline
├── Knowledge/ (3) — Creation, persistence, query
├── Notion/ (11) — Sync, repositories, builders, mappers
├── Orchestrator/ (2) — Digest + Knowledge orchestrators
├── Slack/ (1) — Slack DM delivery
├── Subscription/ (3) — Facade + query/persistence
├── Transport/ (2) — Message transport interface + Slack impl
└── Root (2) — CategoryService + EmojiConverterAPI Routes: 18 across 6 controllers
All routes authenticated with bearer token (ROLE_ADMIN).
Code Statistics
| Metric | Value |
|---|---|
| Backend files | ~80+ (entities, services, controllers, DTOs, mappers, constants, enums) |
| Slack bot files | ~15 (handlers, views, services, app entry) |
| Total services | 38 |
| Total entities | 5 |
| Total API routes | 18 |
| Database | SQLite (8 tables) |
| AI provider | OpenAI + OpenRouter |
| Testing | PHPUnit (backend) + Playwright (E2E) |
Lessons Learned
What Worked Well
- Layered architecture — The Orchestrator → Service → Repository pattern scaled well from POC to MVP
- SQLite for MVP — Simple, no external dependencies, fast enough for the team's scale
- Slack as UI — Zero frontend framework needed, native experience
- AI provider abstraction —
AiProviderInterfaceallowed swapping OpenAI for OpenRouter seamlessly - Lazy AI generation — Generating summaries only when needed (digest time) kept knowledge creation fast
What Changed from Plan
- No entity renames — Knowledge stayed as Knowledge (not renamed to Resource), Category stayed as Category (not ThematicList)
- No Redis/Messenger — Synchronous SQLite proved sufficient; adding these would be premature optimization
- No per-role summaries — Single summary per item was simpler and still valuable
- No PromptTemplate entity — Hardcoded prompt works well for one summary type
- No separate Tag entity — JSON arrays on entities were simpler and sufficient