rahul7star commited on
Commit
ddc835b
Β·
verified Β·
1 Parent(s): b12818b

Create design.md

Browse files
Files changed (1) hide show
  1. design.md +375 -0
design.md ADDED
@@ -0,0 +1,375 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Mycelium β€” Design Document
2
+
3
+ ## Problem
4
+
5
+ Personal knowledge is broken at the retrieval end, not the capture end.
6
+
7
+ People save articles, take screenshots, bookmark links, and write notes constantly. The tooling for capture is abundant β€” browsers, notes apps, read-later lists. The problem is that nothing brings it back. Saved things become graveyards. The forgetting curve wins. When bored, the path of least resistance is algorithmic feeds that optimise for engagement, not for your actual interests.
8
+
9
+ The result: you know less than you've learned, you act on less than you've saved, and your attention is rented to platforms rather than invested in yourself.
10
+
11
+ **Three specific failures:**
12
+
13
+ 1. **Scattered capture** β€” text in Keep, links in Pocket, screenshots in Camera Roll, long-form in Obsidian. No single place, no unified signal.
14
+ 2. **No recall** β€” nothing surfaces saved content at the right time, in the right mood, with the right context.
15
+ 3. **No connections** β€” two related ideas captured months apart never meet. Patterns in your own knowledge are invisible.
16
+
17
+ ---
18
+
19
+ ## Solution
20
+
21
+ **Mycelium** is a local-first personal knowledge agent. Like mycelium β€” the underground fungal network that connects trees and surfaces nutrients where they're needed β€” it sits beneath your daily attention, quietly connecting what you've captured and surfacing it back when it matters.
22
+
23
+ **Core loop:**
24
+
25
+ ```
26
+ Capture β†’ Enrich β†’ Store β†’ Connect β†’ Surface β†’ Feedback β†’ (back to Enrich)
27
+ ```
28
+
29
+ It is not a notes app. It does not replace Obsidian or Keep. It is the active layer on top β€” the thing that reads what you've saved and decides what to do with it.
30
+
31
+ **Design principles:**
32
+ - Local-first. Your knowledge stays on your machine.
33
+ - Capture everything, surface selectively.
34
+ - Bad enrichment is worse than no enrichment β€” quality gates matter.
35
+ - The system should get smarter from use, not just from configuration.
36
+
37
+ ---
38
+
39
+ ## Architecture
40
+
41
+ ```mermaid
42
+ flowchart TD
43
+ subgraph Input
44
+ A1[Text / Note]
45
+ A2[Link / URL]
46
+ A3[Screenshot / Image]
47
+ A4[Import\nObsidian Β· Keep Β· Notion]
48
+ end
49
+
50
+ subgraph Capture
51
+ B[Capture Layer\nreceives + deduplicates]
52
+ end
53
+
54
+ subgraph Enrichment
55
+ C0[Pre-filter\ndedup Β· length Β· language]
56
+ C2[Summary]
57
+ C3[Concepts]
58
+ C4[Labels]
59
+ C5[Intent]
60
+ C6[Embeddings]
61
+ C7[Post-filter\nquality gate]
62
+ end
63
+
64
+ subgraph Storage
65
+ D1[(SQLite\nmetadata Β· enrichment Β· events)]
66
+ D2[/filesystem\ncontent Β· images/]
67
+ end
68
+
69
+ subgraph Connections
70
+ E[Semantic Similarity\nfind related captures]
71
+ end
72
+
73
+ subgraph Surface
74
+ F[Surface Engine\npick right item Β· right time]
75
+ F1[Spaced Repetition]
76
+ F2[Daily Brief]
77
+ F3[Digest]
78
+ F4[Export]
79
+ end
80
+
81
+ subgraph Feedback
82
+ G[Signal Layer\nexplicit + implicit]
83
+ end
84
+
85
+ CTX[Context\ntime Β· mood Β· activity]
86
+ LCM[Lifecycle Management\narchival Β· decay Β· graduation]
87
+
88
+ A1 & A2 & A3 & A4 --> B
89
+ CTX --> B
90
+ B --> C0 --> C2 --> C3 --> C4 --> C5 --> C6 --> C7
91
+ C7 --> D1
92
+ C7 --> D2
93
+ D1 --> E
94
+ E --> D1
95
+ D1 --> LCM
96
+ LCM --> D1
97
+ D1 & E --> F
98
+ CTX --> F
99
+ F --> F1 & F2 & F3 & F4
100
+ F --> G
101
+ G --> F
102
+ G -.->|re-enrichment trigger| C5
103
+ ```
104
+
105
+ ---
106
+
107
+ ## Components
108
+
109
+ ### Capture
110
+ **Responsibility:** Receive raw input across modalities. Store original material to filesystem immediately.
111
+ **Inputs:** Text, URL, image file, import batch
112
+ **Outputs:** Raw capture record + original material stored to filesystem
113
+ **Failure mode:** Duplicate captures inflate the KB; noisy captures degrade enrichment quality
114
+ **Key decision:** Capture is permissive β€” let everything in. Filtration happens in Enrichment, not here.
115
+
116
+ ---
117
+
118
+ ### Extraction & Enrichment
119
+ **Responsibility:** Transform raw captures into structured knowledge. The most critical component β€” bad output here corrupts everything downstream.
120
+
121
+ Two-stage filtration wraps enrichment:
122
+
123
+ **Stage 1 β€” Pre-filter (no LLM, cheap):**
124
+ - Dedup fingerprint check (URL hash + content hash + embedding similarity)
125
+ - Minimum length / language check
126
+ - Obvious spam rejection
127
+
128
+ **Stage 2 β€” Enrichment (LLM):**
129
+
130
+ | Sub-component | What it produces | Failure mode |
131
+ |---|---|---|
132
+ | Summary | 1-2 sentence distillation | Too generic β†’ useless for search and connections |
133
+ | Concepts | Abstract ideas that span captures | Hallucination; derive from clustering, not per-capture generation |
134
+ | Labels | Broad user-visible categories | Model drift from user's own taxonomy |
135
+ | Intent | `learn / act / reference / ephemeral` | Wrong classification = item permanently misrouted |
136
+ | Embeddings | Semantic vector (model-versioned) | Incompatible across model changes |
137
+
138
+ **Stage 3 β€” Post-filter (quality gate):**
139
+ - Reject if summary is empty or generic
140
+ - Reject if intent confidence is below threshold
141
+ - Flag for manual review rather than silent drop
142
+
143
+ **Intent is the most consequential field.** A `learn` item classified as `ephemeral` is gone. Must be correctable.
144
+
145
+ ---
146
+
147
+ ### Storage
148
+ **Responsibility:** Single source of truth. Persist everything.
149
+
150
+ ```
151
+ SQLite (mind.db)
152
+ β”œβ”€β”€ captures metadata, enrichment fields, lifecycle state
153
+ β”œβ”€β”€ events append-only signal log (surfaced, skipped, done, dwell)
154
+ └── concepts concept vocabulary table (grows from tag clustering)
155
+
156
+ filesystem (storage/)
157
+ β”œβ”€β”€ content/ {capture_id}.txt β€” page text, OCR output
158
+ └── images/ {capture_id}.ext β€” managed image files
159
+ ```
160
+
161
+ **Key decisions:**
162
+ - SQLite for structured data and query flexibility; filesystem for raw material. No DB blobs.
163
+ - Concepts get their own table β€” not a JSON field β€” to enable querying.
164
+ - Orphan prevention: capture deletion must clean both DB and filesystem.
165
+ - `raw` text is not stored in DB for links/images β€” content_path points to filesystem.
166
+
167
+ ---
168
+
169
+ ### Connections
170
+ **Responsibility:** Find semantic relationships between captures using embedding similarity.
171
+ **Inputs:** New capture embedding + all existing embeddings
172
+ **Outputs:** `related_ids` list stored on each capture
173
+ **Failure mode:** Surface-level similarity without conceptual link (false connections); vocabulary mismatch misses real connections
174
+ **Scale limit:** Per-pair cosine scan degrades beyond ~1000 captures β†’ migrate to sqlite-vec at that point.
175
+
176
+ ---
177
+
178
+ ### Context
179
+ **Responsibility:** Provide situational signal to Capture and Surface.
180
+ **Signals:** Time of day, day of week, self-reported mood (optional dropdown)
181
+ **Used by:** Surface (pick mood-appropriate items); Capture (timestamp context stored with capture)
182
+ **Mood capture:** Optional dropdown in UI β€” no selection = no context signal. Time-of-day inferred automatically.
183
+
184
+ ---
185
+
186
+ ### Surface
187
+ **Responsibility:** Pick the right item at the right time and deliver it.
188
+
189
+ **Scoring inputs:** intent weight Β· time since last surfaced Β· novelty Β· context Β· feedback history
190
+ **Outputs:**
191
+
192
+ | Mode | What it does |
193
+ |---|---|
194
+ | Spaced Repetition | `learn` items resurface at growing intervals |
195
+ | Daily Brief | Morning digest of `act` items + one `learn` |
196
+ | Digest | Weekly summary of what was captured and what's aging |
197
+ | Export | Push to Obsidian / markdown file |
198
+
199
+ **Invariant:** Surface must never return empty. If all items are below threshold, return best available.
200
+
201
+ ---
202
+
203
+ ### Feedback / Signal
204
+ **Responsibility:** Capture user behaviour and propagate it back into the system.
205
+
206
+ **Explicit signals:**
207
+ - `done` β€” item completed or learned
208
+ - `skip` β€” not now
209
+ - `correct` β€” user overrides intent/summary
210
+
211
+ **Implicit signals:**
212
+
213
+ | Signal | Threshold | Action |
214
+ |---|---|---|
215
+ | Dwell time < 2s + skip | β€” | Wrong item entirely |
216
+ | Dwell time > 15s + skip | β€” | Right content, wrong time |
217
+ | Skip streak | 3Γ— | Trigger intent re-evaluation |
218
+ | Done on first surface | β€” | May have been wrong `act`; flag |
219
+
220
+ **Storage:** Raw events in `events` table. Aggregates computed on read.
221
+ **Re-enrichment:** Skip streak β‰₯ 3 β†’ re-run intent classification with behavioural context appended to prompt.
222
+
223
+ ---
224
+
225
+ ### Lifecycle Management
226
+ **Responsibility:** Manage how captures age, graduate, and retire.
227
+
228
+ ```mermaid
229
+ stateDiagram-v2
230
+ [*] --> Active : captured
231
+ Active --> Active : surfaced
232
+ Active --> Done : marked done
233
+ Active --> Archived : decayed / manually archived
234
+ Done --> Archived : after retention period
235
+ Archived --> [*]
236
+ ```
237
+
238
+ **Decay rules (proposed):**
239
+ - `ephemeral` β†’ auto-archive after 7 days
240
+ - `act` β†’ surface daily; flag as stale after 30 days without done
241
+ - `learn` β†’ spaced repetition; never auto-archive
242
+ - `reference` β†’ surface on demand; archive after 1 year without access
243
+
244
+ ---
245
+
246
+ ### Correction
247
+ **Responsibility:** Allow explicit user override of enrichment fields.
248
+ **What can be corrected:** intent, summary, labels, concepts
249
+ **When:** At any point post-capture
250
+ **Effect:** Updates Storage; resets feedback counters; re-runs embedding if summary changed
251
+
252
+ ---
253
+
254
+ ## Data Model
255
+
256
+ ```mermaid
257
+ erDiagram
258
+ captures {
259
+ int id PK
260
+ text type
261
+ text source_url
262
+ text content_path
263
+ text summary
264
+ text intent
265
+ text labels
266
+ text embedding
267
+ text embedding_model
268
+ text related_ids
269
+ text lifecycle_state
270
+ text created_at
271
+ text last_surfaced_at
272
+ int reviewed
273
+ }
274
+
275
+ events {
276
+ int id PK
277
+ int capture_id FK
278
+ text event
279
+ text value
280
+ text created_at
281
+ }
282
+
283
+ concepts {
284
+ int id PK
285
+ text name
286
+ text description
287
+ int capture_count
288
+ text created_at
289
+ }
290
+
291
+ capture_concepts {
292
+ int capture_id FK
293
+ int concept_id FK
294
+ }
295
+
296
+ captures ||--o{ events : "has"
297
+ captures ||--o{ capture_concepts : "tagged with"
298
+ concepts ||--o{ capture_concepts : "applied to"
299
+ ```
300
+
301
+ ---
302
+
303
+ ## Pipeline Flow
304
+
305
+ ```mermaid
306
+ sequenceDiagram
307
+ participant U as User
308
+ participant C as Capture
309
+ participant PF as Pre-filter
310
+ participant E as Enrichment
311
+ participant QG as Quality Gate
312
+ participant S as Storage
313
+ participant CN as Connections
314
+ participant SF as Surface
315
+
316
+ U->>C: text / link / image
317
+ C->>S: store raw content to filesystem
318
+ C->>PF: check dedup Β· length Β· language
319
+ PF-->>C: reject if duplicate or noise
320
+ PF->>E: pass if clean
321
+ E->>E: summary Β· labels Β· intent Β· embed
322
+ E->>QG: check summary quality Β· intent confidence
323
+ QG-->>E: reject or flag if low confidence
324
+ QG->>S: structured capture metadata
325
+ S->>CN: new embedding
326
+ CN->>CN: cosine similarity vs all
327
+ CN->>S: update related_ids
328
+
329
+ U->>SF: "what should I look at?"
330
+ SF->>S: get surfaceable captures
331
+ SF->>SF: score by intent Β· recency Β· context
332
+ SF->>U: top-n with related
333
+ U->>SF: done / skip (+ dwell time)
334
+ SF->>S: log event
335
+ SF->>E: re-enrich if skip streak β‰₯ 3
336
+ ```
337
+
338
+ ---
339
+
340
+ ## Appendix: Key Tradeoffs and Decisions
341
+
342
+ ### Embedding model versioning
343
+ **Decision:** Store `embedding_model` alongside every embedding vector.
344
+ **Why:** Embeddings from different models live in incompatible vector spaces β€” cosine similarity across models is meaningless. When model changes, re-embed all captures.
345
+ **Tradeoff:** Re-embedding is expensive but unavoidable. Mixing model versions silently would corrupt connections.
346
+
347
+ ### Filtration gate placement (two-stage)
348
+ **Decision:** Pre-filter (cheap, no LLM) before enrichment; post-filter (quality gate) after enrichment.
349
+ **Why:** Can't filter on quality without understanding content, but shouldn't waste LLM compute on obvious garbage.
350
+ **Tradeoff:** Two passes adds complexity. The alternative (single gate after enrichment) wastes compute on noise; single gate before enrichment misses quality failures.
351
+
352
+ ### Raw content storage (filesystem, not DB)
353
+ **Decision:** Page text and images go to `storage/content/` and `storage/images/`, not SQLite.
354
+ **Why:** Page content can be 50k+ characters. Inline in DB means every query drags that weight. SQLite is the hot path for scoring, surfacing, and feed queries.
355
+ **Tradeoff:** Orphan risk on deletion. Mitigated by a single `delete_capture()` function that cleans both.
356
+
357
+ ### Concepts: derive, don't generate
358
+ **Decision:** Don't extract concepts per-capture. Start with tag clustering (Option C); graduate to constrained vocabulary (Option B) as KB grows.
359
+ **Why:** Per-capture concept extraction hallucinates. LLM has no grounding when looking at one item in isolation. Clustering over real accumulated data reduces hallucination surface significantly.
360
+ **Tradeoff:** Concepts are unavailable until enough tags accumulate. Acceptable β€” wrong concepts are worse than no concepts.
361
+
362
+ ### Deduplication fingerprint
363
+ **Decision:** Three-layer check β€” URL hash (exact) + content hash (exact) + embedding similarity threshold (fuzzy).
364
+ **Why:** URL hash misses same content at different URLs. Content hash misses paraphrases. Embedding similarity catches near-duplicates but needs the other two to avoid false positives.
365
+ **Tradeoff:** Three checks add latency at capture time. Worth it β€” duplicates degrade KB quality permanently.
366
+
367
+ ### SQLite over specialised stores
368
+ **Decision:** SQLite for all structured data until query patterns are known.
369
+ **Why:** Query patterns are unknown early. SQLite is flexible, requires no infrastructure, and handles the expected load.
370
+ **Migration trigger:** Embedding cosine scan degrades beyond ~1000 captures β†’ add sqlite-vec extension. Schema stays the same, scan becomes indexed.
371
+
372
+ ### Mood/context capture
373
+ **Decision:** Optional dropdown in UI. No selection = no context signal. Time-of-day inferred automatically.
374
+ **Why:** Forced mood capture adds friction and will be skipped. Optional preserves signal quality β€” only captured when user is willing.
375
+ **Tradeoff:** Sparse signal early. Time-of-day inference compensates until enough explicit mood data accumulates.