MVP: Knowledge to AI Summaries to Digest — Complete E2E Flow
This document describes the complete end-to-end flow from sharing knowledge via Slack through AI summary generation, Notion synchronization, and digest distribution. This is the canonical reference for how the system works.
1. Knowledge Sharing (Input)
Users share knowledge via Slack using one of these entry points:
| Entry Point | Trigger | Handler |
|---|---|---|
| Message Shortcut | Right-click message → "Add to YapHub" | shortcuts.ts |
| Global Shortcut | Lightning bolt → "Quick Add to YapHub" | shortcuts.ts |
| App Home Button | Click "Add Knowledge" in App Home | actions.ts |
What the user sees:
- A modal opens with fields: Title, Content, Category (select), Tags, URL
- For message shortcuts, content and URL are pre-filled from the Slack message
- User submits the form
What happens on submission:
Slack Bot calls the backend API:
POST /api/knowledge
Body: { title, content, tags, categoryId, url, userId, targetGroups }2. Knowledge Creation
Backend (KnowledgeOrchestrator::createKnowledge):
- Resolve Category →
CategoryService::findById(categoryId) - Map request →
KnowledgeMapper::fromCreateRequest() - Extract URL content (if URL present) →
ContentExtractionService::extractContent(url)- Fetches page HTML
- Extracts text with Readability parser
- Appends extracted content to user's text
- Persist →
KnowledgePersistenceService::create()→ SQLite - Sync to Notion (best-effort) →
NotionSyncService::syncKnowledgeToNotion()- Creates Notion page with title, content, tags, status, URL, category
- Stores
notionIdandnotionUrlon the Knowledge entity
3. Digest Generation
When enough knowledge items accumulate, users generate digests via Slack UI or API.
Trigger
POST /api/digests/generate
Body: { categoryId, preset: "7 days", limit: 20, distributeNow: false }The 7-Step Pipeline (DigestOrchestrator::generateDigest)
Step 1 — Resolve CategoryCategoryService::findById(categoryId) — look up the target category.
Step 2 — Parse Date RangeDateRangeParser::parse("7 days") — converts user input into concrete start/end dates. Supports Dutch ("laatste week") and English ("7 days").
Step 3 — Query KnowledgeKnowledgeRepository::findByCategoryAndDateRange(categoryId, start, end, limit) — fetch matching items.
Step 4 — Generate AI Highlights For each knowledge item, KnowledgeHighlightGenerator::generateHighlight():
- Check if
highlightalready exists (lazy — skip if present) - Build prompt:
sprintf(HIGHLIGHT_SUMMARY_PROMPT, title, url, content) - Call
AiProviderInterface::generateCompletion(prompt, options)with temperature 0.3, max 150 tokens - Store result in
Knowledge.highlight - Fallback: truncate content to 200 chars on AI failure
Step 5 — Format MarkdownDigestContentFormatter::formatDigestContent() — builds Dutch Markdown:
# DevOps & Infrastructure Kennisdigest
Periode: 01/05/2026 - 08/05/2026 | Gegenereerd op 08/05/2026
5 items in deze digest
---
### [Kubernetes Best Practices](https://notion.so/...) (3 minuten)
AI-generated 2-3 sentence Dutch summary...
`DevOps` `Kubernetes`Step 6 — Create EntityDigestCreationService::createFromGenerationData():
- Auto-generate title: "Digest: {Category} ({dateRange})"
- Set content, statistics (highlightCount, days), knowledgeHighlights JSON snapshot
- Link knowledge items via ManyToMany
Step 7 — Persist & SyncDigestPersistenceService::persistAndSync():
- Save to SQLite
NotionSyncService::syncDigestToNotion()— create Notion page with properties + embed full Markdown content
4. Digest Distribution
Trigger
POST /api/digests/{id}/distribute
Body: { initiatorUserId: "U01234567" }Recipient Resolution
- Query active subscribers for the digest's category:
SubscriptionQueryService::findActiveSubscriptionsWithUsers(categoryId) - Add the initiating user if not already in the list
- Deduplicate by userId
Delivery Flow
For each recipient:
- Convert Markdown to Slack mrkdwn —
SlackDigestDeliveryServiceconverts links, bold, headers - Send via Slack DM —
SlackMessageTransportPOSTs to the Slack bot's Express API - Bot delivers DM —
client.chat.postMessage()sends the formatted digest - Log delivery —
DigestDeliveryLogService::logDelivery()creates/updatesDigestDeliveryentity
Delivery Tracking
Each delivery is tracked in a DigestDelivery entity:
- Status: pending → sent (or failed)
- Retry support: max 3 attempts via
canRetry() - Failed deliveries can be retried:
POST /api/digests/{id}/retry-failed
Sync to Notion
Distribution results are synced back to Notion — the digest page is updated with recipient list and distributed status.
5. Subscription System
Users subscribe to categories to receive digests:
| Action | Endpoint | Behavior |
|---|---|---|
| Subscribe | POST /api/subscriptions/subscribe | Creates Subscription (userId + categoryId) |
| Unsubscribe | POST /api/subscriptions/unsubscribe | Soft-delete: isActive = false, unsubscribedAt = now |
| List user subs | GET /api/subscriptions/user/{userId} | Returns active subscriptions |
| List subscribers | GET /api/subscriptions/category/{categoryId}/subscribers | Returns subscriber user IDs |
Unique constraint ensures one subscription per user per category.
6. Digest Scheduling
Categories with digest settings can generate digests automatically:
DigestSchedulerService::processScheduledDigests()checks all digest-enabled categories- For each: compares
lastDigestAt+ frequency interval against current time - Checks configured day of week and time
- If due → triggers the full 7-step pipeline
Frequency options: weekly (7d), biweekly (14d), monthly (30d), quarterly (90d).
Complete Flow Diagram
┌─────────────────────────────────────────────────────────────────────┐
│ USER VIA SLACK │
│ [Message Shortcut] [Global Shortcut] [App Home] │
│ Fill: Title, Content, Category, Tags, URL │
└───────────────────────────────┬─────────────────────────────────────┘
▼
┌───────────────────────────────────────────────────────────────────┐
│ KNOWLEDGE CREATION │
│ 1. Resolve Category │
│ 2. Extract URL content (if URL present) │
│ 3. Persist to SQLite │
│ 4. Sync to Notion (page + properties) │
└───────────────────────────────┬───────────────────────────────────┘
▼
┌───────────────┐
│ ACCUMULATE │
│ Knowledge │
│ Items │
└───────┬───────┘
▼
┌───────────────────────────────────────────────────────────────────┐
│ DIGEST GENERATION (7 steps) │
│ 1. Resolve Category │
│ 2. Parse Date Range │
│ 3. Query Knowledge by category + date range │
│ 4. Generate AI Highlights (lazy — skip if exists) │
│ 5. Format as Dutch Markdown │
│ 6. Create Digest entity with snapshot │
│ 7. Persist to SQLite + sync to Notion │
└───────────────────────────────┬───────────────────────────────────┘
▼
┌───────────────────────────────────────────────────────────────────┐
│ DIGEST DISTRIBUTION │
│ 1. Resolve recipients (category subscribers + initiator) │
│ 2. For each recipient: │
│ - Convert Markdown → Slack mrkdwn │
│ - Send via Slack DM │
│ - Log delivery status (DigestDelivery entity) │
│ 3. Update digest status to "sent" │
│ 4. Sync distribution results to Notion │
└───────────────────────────────────────────────────────────────────┘Key Technical References
| Component | File | Purpose |
|---|---|---|
| Knowledge Orchestrator | backend/src/Service/Orchestrator/KnowledgeOrchestrator.php | Knowledge creation + Notion sync |
| Digest Orchestrator | backend/src/Service/Orchestrator/DigestOrchestrator.php | 7-step digest pipeline |
| Highlight Generator | backend/src/Service/Digest/KnowledgeHighlightGenerator.php | AI summary generation |
| AI Provider | backend/src/Service/Ai/OpenAIService.php | OpenAI/OpenRouter API calls |
| Distribution Service | backend/src/Service/Digest/DigestDistributionService.php | Subscriber resolution + delivery |
| Slack Delivery | backend/src/Service/Slack/SlackDigestDeliveryService.php | Markdown → mrkdwn + DM sending |
| Subscription Service | backend/src/Service/Subscription/SubscriptionService.php | Subscribe/unsubscribe facade |
| Scheduler | backend/src/Service/Digest/DigestSchedulerService.php | Cron-based digest scheduling |
| Notion Sync | backend/src/Service/Notion/NotionSyncService.php | Bidirectional sync for all entities |