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 → "Opslaan in Kennishub" | shortcuts.ts → handleSaveMessageShortcut |
| Global Shortcut | Lightning bolt → "Snel Kennis Toevoegen" | shortcuts.ts → handleQuickAddShortcut |
| App Home Button | Click "Kennis Toevoegen" in App Home | home/manage.ts |
| URL Detection | Bot detects URL in channel message | events.ts |
| Slash Command | /knowledge add <url> | commands.ts |
| Save URL/File/Reaction | Various Slack save flows | saveHandlers.ts |
What the user sees in Slack:
- A modal opens with fields: Title, Content, Category (select), Target Groups (multi-select), Tags, URL, and a checkbox "Genereer AI-samenvattingen"
- The modal pre-fills target groups from the first selected category's defaults
- User can add or remove target groups (explicit selection)
- User submits the form
What happens on submission:
Slack Bot (knowledge/submissions.ts) calls the backend API:
POST /api/knowledge
Body: {
title, content, tags, category_id,
target_group_ids: [...], // explicit selections (or omitted = inherit from category)
generate_ai_summaries: true/false,
user_id: "U_SLACK_ID"
}2. Knowledge Creation & Target Group Inheritance
Backend (KnowledgeOrchestrator::createKnowledge):
- Resolve Category →
CategoryResolutionService::resolve(categoryId) - Inherit Target Groups from category (if user didn't explicitly provide any):
- Start with category's default target groups (active only)
- If user explicitly selected target groups → merge with category defaults (no duplicates)
- If user sent empty array explicitly → clear all target groups
- Map →
KnowledgeMapper::fromCreateRequest()viaMapperRegistry - Persist →
KnowledgePersistenceService::create()→ PostgreSQL - Sync to Notion (best-effort) →
NotionSyncKnowledge::syncToNotion()→ Notion API- Creates a Notion page with title, content, category relation, tags, target groups
- Stores
notionIdon the Knowledge entity
Target Group Inheritance Rules:
Category "AI Resources" has defaults: [Developers, CEO]
User selects: [Product Manager]
Result: Knowledge gets [Developers, CEO, Product Manager] (merged, deduplicated)Category "AI Resources" has defaults: [Developers, CEO]
User selects: nothing (omits target_group_ids)
Result: Knowledge gets [Developers, CEO] (inherited from category)3. AI Summary Generation
If the user checked "Genereer AI-samenvattingen", the system generates summaries for each target group on the knowledge item.
3A. Trigger from Slack Submission
Slack Bot calls:
POST /api/knowledge/{id}/summaries/generate-allBackend (KnowledgeOrchestrator::generateAllSummaries):
For each target group on the knowledge item:
- Resolve Template →
PromptBuilderService::getTemplateForTargetGroup(targetGroup, category)
3B. Prompt Template Resolution Chain (Priority Order)
1. Category-specific template → findByTargetGroupAndCategory(tg, category)
Example: "AI Resources" + "Developers" → specific template
2. Global template (no category) → findByTargetGroupAndCategory(tg, null)
Example: "Developers" → global developer template
3. System default template → findSystemDefault()
Example: fallback basic template for any target groupIf none found → throws InvalidArgumentException ("No active prompt template found")
3C. Prompt Building
PromptBuilderService::buildPrompt():
- Resolve template via inheritance chain above
- Build system variables from Knowledge + TargetGroup entities:
{{title}}— Knowledge title{{content}}— Knowledge content + optional extra context{{url}}— Source URL{{url_content}}— Extracted body text from URL (viaContentExtractionService){{category}}— Category name{{target_group_name}}— Target group name{{target_group_description}}— Target group description
- Render template with Handlebars-style substitution using placeholders like
{{var}}and conditional blocks for optional data - Prepend Dutch language directive:
"IMPORTANT: Respond ONLY in Dutch language."
3D. AI Completion
AiSummaryOrchestrator::generateSummary():
- Call
SymfonyAiProvider::generateCompletion(prompt, options) - Route through:
CustomGatewayPlatform → AuthOverrideHttpClient → AI Gateway - Track: model, tokens, cost, duration
- Create
AiSummaryentity viaSummaryPersistenceService::createSummary()
3E. Notion Sync for Summaries (3-Gate Pattern)
AiSummaryOrchestrator::syncToNotion():
GATE 1: Knowledge must have notionId
→ If null → skip Notion sync (summaries exist locally only)
GATE 2: Create summary page in Notion + persist notionId
→ NotionAiSummaryService::createSummaryPage(summary, knowledge)
→ summary.setNotionId(notionPageId)
→ flush() — CRITICAL: must complete before Gate 3
GATE 3: Link all summaries to knowledge page
→ Query all summaries with notionId for this knowledge
→ NotionAiSummaryService::linkSummariesToKnowledgePage()
→ Creates Notion relation on knowledge page pointing to all AI summaries4. User Notification
After AI summaries are generated, the user gets notified:
AiSummaryOrchestrator::notifySummaryGeneration():
- Wraps Knowledge + Summaries in
AiSummaryDeliverableDTO - Calls
DistributionService::distribute()to the original user - Routes through the 4-layer delivery architecture:
- Distribution → Channel → Formatter → Transport
- User receives a Slack DM with:
- Knowledge item header
- AI summary text (with "AI Generated" badge)
- "View Original" action button
Additionally from the Slack bot:
The sendSummaryConfirmation() function sends a rich confirmation:
- ✅ Knowledge item saved
- 🧠 AI summaries generated: N summaries
- Per-summary details: target group name, content preview
- Edit/Regenerate action buttons
5. Summary Management (User Actions)
Users can manage their AI summaries:
| Action | Flow | Handler |
|---|---|---|
| Edit Summary | Slack modal → PUT /api/summaries/{id} | AiSummaryOrchestrator::editSummary() |
| Regenerate | Slack modal (with extra context/template) → POST /api/summaries/{id}/regenerate | AiSummaryOrchestrator::regenerateSummary() |
| Edit Target Groups | Slack modal → merge target groups → regenerate | handleEditTargetGroups() |
Edit updates content + syncs edit to Notion (best-effort). Regenerate creates a new version with incremented version number, optional extra context, or different template.
6. Notion Synchronization Summary
All entities maintain bidirectional or one-way sync with Notion:
| Entity | Direction | Sync Service | Notion Page Content |
|---|---|---|---|
| Knowledge | Bidirectional | NotionSyncKnowledge | Title, content, URL, tags, category relation, target groups, AI summaries relation |
| AI Summary | Local → Notion | NotionAiSummaryService | Summary content, target group, knowledge relation, template link |
| Category | Bidirectional | NotionSyncCategory | Name, description, target groups, icon |
| Prompt Template | Notion → Local | SyncPromptTemplatesFromNotionCommand | Template text, target group, category |
| Digest | Local → Notion | DigestNotionSyncService | Title, summary, content, knowledge items, AI summaries, categories |
7. Digest Generation
When enough knowledge items accumulate, digests can be generated manually (via Slack UI or API) or periodically (via cron/scheduler).
Trigger
POST /api/digests/generate
Body: {
categoryId: 3, // required — digest is category-scoped
dateRange: "7 days" // relative or absolute
}Or via Slack: User clicks "Genereer Digest" in category overflow menu → handleGenerateDigest().
Generation Flow (DigestOrchestrator::generateDigest)
- Parse Date Range →
DigestQueryService::parseDateRange() - Query Knowledge Items →
DigestQueryService::findKnowledgeForDigest()— filter by category, date range - Ensure AI Summaries Exist →
DigestSummaryOrchestrator::ensureAllSummariesExist()— generates any missing summaries - Generate Digest Data →
DigestGenerationService::generateDigestData()— groups items by category, calculates statistics - Generate Digest-Level Summary →
DigestAiSummaryGenerator::generateDigestSummary()— creates an overarching summary using the digest template - Format Content →
DigestContentFormatter::formatDigestContent()
Digest Content Structure
📊 Statistics (top section):
- Total items: 12
- Categories: 3
- AI summaries: 12
- Estimated read time: ~15 min
📋 Knowledge Items:
For each item:
- Title + tags
- 1-2 sentence summary (generated on digest creation)
- Link to Notion knowledge page
🧠 AI Summaries by Target Group:
- "Samenvatting voor Developers" → [Notion link]
- "Samenvatting voor CEO" → [Notion link]Persist & Sync
DigestPersistenceService::create()→ PostgreSQLDigestNotionSyncService::syncDigestToNotion()→ Notion (best-effort)- Creates Notion page with full content
- Links to knowledge items and AI summaries
8. Digest Distribution
Recipient Resolution (DigestRecipientResolver::resolveRecipients)
Recipients are resolved from two subscription sources + deduplicated:
- Category Subscribers →
CategorySubscriptionRepository::findActiveSubscribers()for each category in the digest - Target Group Subscribers →
TargetGroupSubscriptionRepository::findActiveSubscribers()for each target group in the digest - Merge + Deduplicate user IDs
- Add Initiator (if provided)
Example
Digest for Category "AI Resources" with Target Group "Developers":
Category "AI Resources" subscribers: [Jack, Joanne]
Target Group "Developers" subscribers: [Jack, Mike]
Target Group "CEO" subscribers: [Joanne]
→ Merged recipients: [Jack, Joanne, Mike] (deduplicated)Delivery Flow (DistributionService::distribute)
For each recipient:
- Resolve Preferred Channel →
UserDeliveryPreferenceService::getEffectiveChannel()(default: Slack) - Create Channel →
DeliveryChannelFactory::createChannel()→SlackDeliveryChannel|EmailDeliveryChannel|NotionDeliveryChannel - Format →
SlackBlockFormatter::formatBlocks()— Block Kit JSON with stats, items, summaries, links - Transport →
SlackMessageTransport::send()→ Slack Bot API → Slack DM to user - Log →
DigestDeliveryLogService::logDelivery()→ PostgreSQL
Delivery Channels
- Slack (implemented) — full Block Kit rich messages
- Email (stub) — returns "not yet implemented"
- Notion (stub) — returns "not yet implemented"
9. Subscription System
Users can subscribe to categories and/or target groups:
Category Subscription
Via Slack overflow menu → "Subscribe" → POST /api/subscriptions → CategorySubscriptionOrchestrator::subscribeUser() → refresh Slack Home
Target Group Subscription
Via Slack App Home → "Subscribe to Target Group" → POST /api/target-group-subscriptions → TargetGroupSubscriptionOrchestrator::subscribeToTargetGroup() → refresh Slack Home
Effect on Digests
When a digest is generated for a target group, all users subscribed to that target group OR to any category in the digest receive the digest.
Appendix: Complete Flow Diagram
┌─────────────────────────────────────────────────────────────────────┐
│ USER VIA SLACK │
│ [Share Message] [Quick Add] [URL Detection] [Slash Command] │
│ Select: Category, Target Groups, ✅ Generate AI Summaries │
└───────────────────────────────┬─────────────────────────────────────┘
▼
┌───────────────────────────────────────────────────────────────────┐
│ KNOWLEDGE CREATION │
│ 1. Resolve Category → inherit default target groups │
│ 2. Merge user's explicit target groups (deduplicate) │
│ 3. Persist to PostgreSQL │
│ 4. Sync to Notion (page + properties + relations) │
└───────────────────────────────┬───────────────────────────────────┘
▼
┌───────────────────────────────────────────────────────────────────┐
│ AI SUMMARY GENERATION (per target group) │
│ For each target group: │
│ 1. Resolve prompt template (category → global → default) │
│ 2. Build prompt: {{title}}, {{content}}, {{url_content}}, etc. │
│ 3. Call AI provider → generate Dutch summary │
│ 4. Persist AI Summary to PostgreSQL │
│ 5. Sync to Notion: create summary page │
│ 6. Link summaries to knowledge page in Notion │
└───────────────────────────────┬───────────────────────────────────┘
▼
┌───────────────────────────────────────────────────────────────────┐
│ USER NOTIFICATION │
│ Send Slack DM to user: summaries generated │
│ User can: Edit, Regenerate (with extra context/template) │
└───────────────────────────────┬───────────────────────────────────┘
▼
┌───────────────┐
│ ACCUMULATE │
│ Knowledge │
│ Items │
└───────┬───────┘
▼
┌───────────────────────────────────────────────────────────────────┐
│ DIGEST GENERATION │
│ (Manual via Slack UI or API, or periodic via cron) │
│ 1. Query knowledge items by target group, category, date range │
│ 2. Ensure all items have AI summaries (generate missing ones) │
│ 3. Generate digest-level summary via AI │
│ 4. Group items by category with statistics │
│ 5. Persist Digest entity to PostgreSQL │
│ 6. Sync Digest to Notion (page with linked items + summaries) │
└───────────────────────────────┬───────────────────────────────────┘
▼
┌───────────────────────────────────────────────────────────────────┐
│ DIGEST DISTRIBUTION │
│ 1. Resolve recipients: │
│ - Category subscribers + Target group subscribers │
│ - Merge + deduplicate │
│ 2. Per recipient: │
│ - Resolve preferred channel (Slack / Email / Notion) │
│ - Format: Block Kit with stats, items, summary links │
│ - Transport: Send via Slack DM │
│ - Log: delivery status to PostgreSQL │
└───────────────────────────────────────────────────────────────────┘Key Technical References
| Component | File | Purpose |
|---|---|---|
| Knowledge Orchestrator | backend/src/Service/Knowledge/KnowledgeOrchestrator.php | Knowledge CRUD + AI summary trigger |
| AI Summary Orchestrator | backend/src/Service/Summary/AiSummaryOrchestrator.php | Summary lifecycle + Notion sync |
| Prompt Builder | backend/src/Service/Summary/PromptBuilderService.php | Template resolution + prompt rendering |
| Digest Orchestrator | backend/src/Service/Digest/DigestOrchestrator.php | Digest generation (distribution separated) |
| Distribution Service | backend/src/Service/Delivery/DistributionService.php | Multi-channel delivery |
| Recipient Resolver | backend/src/Service/Digest/DigestRecipientResolver.php | Subscription-based recipient resolution |
| Slack Submissions | slack/src/handlers/knowledge/submissions.ts | Slack modal → API calls |
| Architecture Reference | docs/ArchitectureLayered.md | Complete 10-layer architecture |
| Architecture Flows | docs/ArchitectureLayeredFlows.md | 33 documented flow diagrams |