Skip to content

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 PointTriggerHandler
Message ShortcutRight-click message → "Opslaan in Kennishub"shortcuts.tshandleSaveMessageShortcut
Global ShortcutLightning bolt → "Snel Kennis Toevoegen"shortcuts.tshandleQuickAddShortcut
App Home ButtonClick "Kennis Toevoegen" in App Homehome/manage.ts
URL DetectionBot detects URL in channel messageevents.ts
Slash Command/knowledge add <url>commands.ts
Save URL/File/ReactionVarious Slack save flowssaveHandlers.ts

What the user sees in Slack:

  1. A modal opens with fields: Title, Content, Category (select), Target Groups (multi-select), Tags, URL, and a checkbox "Genereer AI-samenvattingen"
  2. The modal pre-fills target groups from the first selected category's defaults
  3. User can add or remove target groups (explicit selection)
  4. 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):

  1. Resolve CategoryCategoryResolutionService::resolve(categoryId)
  2. 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
  3. MapKnowledgeMapper::fromCreateRequest() via MapperRegistry
  4. PersistKnowledgePersistenceService::create() → PostgreSQL
  5. Sync to Notion (best-effort) → NotionSyncKnowledge::syncToNotion() → Notion API
    • Creates a Notion page with title, content, category relation, tags, target groups
    • Stores notionId on 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-all

Backend (KnowledgeOrchestrator::generateAllSummaries):

For each target group on the knowledge item:

  1. Resolve TemplatePromptBuilderService::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 group

If none found → throws InvalidArgumentException ("No active prompt template found")

3C. Prompt Building

PromptBuilderService::buildPrompt():

  1. Resolve template via inheritance chain above
  2. 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 (via ContentExtractionService)
  • {{category}} — Category name
  • {{target_group_name}} — Target group name
  • {{target_group_description}} — Target group description
  1. Render template with Handlebars-style substitution using placeholders like {{var}} and conditional blocks for optional data
  2. Prepend Dutch language directive: "IMPORTANT: Respond ONLY in Dutch language."

3D. AI Completion

AiSummaryOrchestrator::generateSummary():

  1. Call SymfonyAiProvider::generateCompletion(prompt, options)
  2. Route through: CustomGatewayPlatform → AuthOverrideHttpClient → AI Gateway
  3. Track: model, tokens, cost, duration
  4. Create AiSummary entity via SummaryPersistenceService::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 summaries

4. User Notification

After AI summaries are generated, the user gets notified:

AiSummaryOrchestrator::notifySummaryGeneration():

  1. Wraps Knowledge + Summaries in AiSummaryDeliverable DTO
  2. Calls DistributionService::distribute() to the original user
  3. Routes through the 4-layer delivery architecture:
    • Distribution → Channel → Formatter → Transport
  4. 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:

ActionFlowHandler
Edit SummarySlack modal → PUT /api/summaries/{id}AiSummaryOrchestrator::editSummary()
RegenerateSlack modal (with extra context/template) → POST /api/summaries/{id}/regenerateAiSummaryOrchestrator::regenerateSummary()
Edit Target GroupsSlack modal → merge target groups → regeneratehandleEditTargetGroups()

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:

EntityDirectionSync ServiceNotion Page Content
KnowledgeBidirectionalNotionSyncKnowledgeTitle, content, URL, tags, category relation, target groups, AI summaries relation
AI SummaryLocal → NotionNotionAiSummaryServiceSummary content, target group, knowledge relation, template link
CategoryBidirectionalNotionSyncCategoryName, description, target groups, icon
Prompt TemplateNotion → LocalSyncPromptTemplatesFromNotionCommandTemplate text, target group, category
DigestLocal → NotionDigestNotionSyncServiceTitle, 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)

  1. Parse Date RangeDigestQueryService::parseDateRange()
  2. Query Knowledge ItemsDigestQueryService::findKnowledgeForDigest() — filter by category, date range
  3. Ensure AI Summaries ExistDigestSummaryOrchestrator::ensureAllSummariesExist() — generates any missing summaries
  4. Generate Digest DataDigestGenerationService::generateDigestData() — groups items by category, calculates statistics
  5. Generate Digest-Level SummaryDigestAiSummaryGenerator::generateDigestSummary() — creates an overarching summary using the digest template
  6. Format ContentDigestContentFormatter::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() → PostgreSQL
  • DigestNotionSyncService::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:

  1. Category SubscribersCategorySubscriptionRepository::findActiveSubscribers() for each category in the digest
  2. Target Group SubscribersTargetGroupSubscriptionRepository::findActiveSubscribers() for each target group in the digest
  3. Merge + Deduplicate user IDs
  4. 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:

  1. Resolve Preferred ChannelUserDeliveryPreferenceService::getEffectiveChannel() (default: Slack)
  2. Create ChannelDeliveryChannelFactory::createChannel()SlackDeliveryChannel | EmailDeliveryChannel | NotionDeliveryChannel
  3. FormatSlackBlockFormatter::formatBlocks() — Block Kit JSON with stats, items, summaries, links
  4. TransportSlackMessageTransport::send() → Slack Bot API → Slack DM to user
  5. LogDigestDeliveryLogService::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/subscriptionsCategorySubscriptionOrchestrator::subscribeUser() → refresh Slack Home

Target Group Subscription

Via Slack App Home → "Subscribe to Target Group" → POST /api/target-group-subscriptionsTargetGroupSubscriptionOrchestrator::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

ComponentFilePurpose
Knowledge Orchestratorbackend/src/Service/Knowledge/KnowledgeOrchestrator.phpKnowledge CRUD + AI summary trigger
AI Summary Orchestratorbackend/src/Service/Summary/AiSummaryOrchestrator.phpSummary lifecycle + Notion sync
Prompt Builderbackend/src/Service/Summary/PromptBuilderService.phpTemplate resolution + prompt rendering
Digest Orchestratorbackend/src/Service/Digest/DigestOrchestrator.phpDigest generation (distribution separated)
Distribution Servicebackend/src/Service/Delivery/DistributionService.phpMulti-channel delivery
Recipient Resolverbackend/src/Service/Digest/DigestRecipientResolver.phpSubscription-based recipient resolution
Slack Submissionsslack/src/handlers/knowledge/submissions.tsSlack modal → API calls
Architecture Referencedocs/ArchitectureLayered.mdComplete 10-layer architecture
Architecture Flowsdocs/ArchitectureLayeredFlows.md33 documented flow diagrams