···11+# ADHD Support Agent - MVP Specification
22+33+## 0) Workflow & Tracking
44+55+**Issue tracking**: Use `bd` (beads) for all task tracking per CLAUDE.md.
66+77+**Step Zero**: Before writing any code, create beads issues for all milestones.
88+99+**Environments**:
1010+| Environment | Where | Letta | Anthropic | Telegram |
1111+|-------------|-------|-------|-----------|----------|
1212+| **Dev** | Mac local | Docker (localhost:8283) | anthropic-proxy (localhost:4001) | Polling or ngrok webhook |
1313+| **Prod** | VPS | Docker (same compose) | anthropic-proxy | Real webhook URL |
1414+1515+---
1616+1717+## 1) Scope
1818+1919+- **Single user**, single primary Letta agent
2020+- **Primary value**: reduce overwhelm; capture → structure; tiny-first-step task initiation; quick recall ("what was I doing?"); tiny wins tracking
2121+- **Style**: warm, slightly irreverent "wise friend" - calm underneath with a glint of mischief; co-conspirators against the chaos
2222+2323+---
2424+2525+## 2) Architecture
2626+2727+```
2828+┌──────────┐ webhook ┌───────────┐ messages ┌─────────┐
2929+│ Telegram │──────────────▶│ Bun │───────────────▶│ Letta │
3030+│ │◀──────────────│ Adapter │◀───────────────│ Agent │
3131+└──────────┘ └───────────┘ └─────────┘
3232+ │ │
3333+ │ tool calls │
3434+ ▼ ▼
3535+ ┌───────────┐ ┌─────────────┐
3636+ │ SQLite │ │ Anthropic │
3737+ │ (items) │ │ via proxy │
3838+ └───────────┘ └─────────────┘
3939+```
4040+4141+**Key pattern**: Tool execution runs in **Bun** via a local dispatcher. Letta orchestrates tool calls; our app executes and returns results.
4242+4343+**Runtime flow:**
4444+1. Telegram update (webhook) → normalize + dedupe on `update_id`
4545+2. Send user message to Letta (single-agent context)
4646+3. If Letta requests tool calls → dispatch locally → return tool results
4747+4. Send final assistant message back to Telegram
4848+4949+### Data Flow: Letta Memory ↔ SQLite
5050+5151+```
5252+┌─────────────────────────────────────────────────────────────────┐
5353+│ LETTA MEMORY │
5454+│ (Agent's working memory - what it "knows" in context) │
5555+│ │
5656+│ • current_focus: "Working on X because Y, connects to Z" │
5757+│ • tiny_wins: { today: 3, this_week: 12, streak_days: 2 } │
5858+│ • open_items: "3 tasks, 1 reminder (summary)" │
5959+│ • last_checkpoint: "Was doing X, wandered to Y" │
6060+│ │
6161+│ Updated by agent after tool calls. Summaries, not full data. │
6262+└─────────────────────────────────────────────────────────────────┘
6363+ ↕ tools bridge the gap
6464+┌─────────────────────────────────────────────────────────────────┐
6565+│ SQLITE (via Drizzle) │
6666+│ (Source of truth - persistent, complete) │
6767+│ │
6868+│ • items: all 500 tasks, notes, reminders │
6969+│ • wins: every "said → did" recorded │
7070+│ • deviations: every wandering captured │
7171+│ │
7272+│ Tools read/write here. Agent queries when it needs specifics. │
7373+└─────────────────────────────────────────────────────────────────┘
7474+```
7575+7676+**Why both?**
7777+- SQLite = filing cabinet (everything, permanent)
7878+- Letta memory = what's on your desk (working memory, context-sized summaries)
7979+- Tools = fetching from the cabinet when needed
8080+8181+---
8282+8383+## 3) Directory Layout
8484+8585+```
8686+src/
8787+├── index.ts # Bootstrap, webhook server, /health
8888+├── config.ts # Env parsing/validation
8989+├── letta.ts # Client, provider + agent bootstrap
9090+├── bot.ts # Telegram handlers → service layer
9191+├── detect.ts # Overwhelm & self-bullying detection
9292+├── tools/
9393+│ ├── dispatcher.ts # Routes Letta tool calls to handlers
9494+│ ├── capture.ts # parse_brain_dump
9595+│ ├── breakdown.ts # break_down_task
9696+│ ├── context.ts # set_current_focus, get_open_items, record_deviation
9797+│ ├── items.ts # save_item, update_item
9898+│ └── wins.ts # record_tiny_win, get_wins_summary
9999+├── db/
100100+│ ├── index.ts # Drizzle client (bun:sqlite)
101101+│ ├── schema.ts # Drizzle schema definitions
102102+│ └── migrations/ # Drizzle migrations (generated)
103103+└── types.ts
104104+105105+drizzle.config.ts # Drizzle config
106106+docker-compose.yml # anthropic-proxy + letta + app (with healthchecks)
107107+docker-compose.dev.yml # Dev overrides (no app container, local volumes)
108108+.env.example
109109+```
110110+111111+---
112112+113113+## 4) Infrastructure
114114+115115+### docker-compose.yml
116116+117117+```yaml
118118+version: '3.8'
119119+120120+networks:
121121+ assistant-net:
122122+ driver: bridge
123123+124124+services:
125125+ anthropic-proxy:
126126+ build:
127127+ context: .
128128+ dockerfile: Dockerfile.anthropic-proxy
129129+ ports:
130130+ - "4001:4001"
131131+ environment:
132132+ - PORT=4001
133133+ - SESSION_SECRET=${ANTHROPIC_PROXY_SESSION_SECRET}
134134+ - CLIENT_ID=9d1c250a-e61b-44d9-88ed-5944d1962f5e
135135+ - REDIRECT_URI=https://console.anthropic.com/oauth/code/callback
136136+ - OAUTH_BASE_URL=https://claude.ai
137137+ - API_BASE_URL=https://api.anthropic.com/v1
138138+ networks:
139139+ - assistant-net
140140+ restart: unless-stopped
141141+ healthcheck:
142142+ test: ["CMD", "curl", "-f", "http://localhost:4001/health"]
143143+ interval: 30s
144144+ timeout: 10s
145145+ retries: 3
146146+147147+ letta:
148148+ image: letta/letta:latest
149149+ ports:
150150+ - "8283:8283"
151151+ environment:
152152+ # OpenAI for embeddings only
153153+ - OPENAI_API_KEY=${OPENAI_API_KEY}
154154+ volumes:
155155+ - letta-data:/var/lib/postgresql/data
156156+ networks:
157157+ - assistant-net
158158+ restart: unless-stopped
159159+ depends_on:
160160+ anthropic-proxy:
161161+ condition: service_healthy
162162+ healthcheck:
163163+ test: ["CMD", "curl", "-f", "http://localhost:8283/v1/health"]
164164+ interval: 30s
165165+ timeout: 10s
166166+ retries: 3
167167+168168+ app:
169169+ build: .
170170+ ports:
171171+ - "3000:3000"
172172+ environment:
173173+ - PORT=3000
174174+ - LETTA_BASE_URL=http://letta:8283
175175+ - TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
176176+ - TELEGRAM_WEBHOOK_URL=${TELEGRAM_WEBHOOK_URL}
177177+ - TELEGRAM_WEBHOOK_SECRET_TOKEN=${TELEGRAM_WEBHOOK_SECRET_TOKEN}
178178+ - ANTHROPIC_PROXY_URL=http://anthropic-proxy:4001/v1
179179+ - ANTHROPIC_PROXY_SESSION_ID=${ANTHROPIC_PROXY_SESSION_ID}
180180+ - OPENAI_API_KEY=${OPENAI_API_KEY}
181181+ volumes:
182182+ - ./data:/app/data # Persist SQLite database
183183+ networks:
184184+ - assistant-net
185185+ restart: unless-stopped
186186+ depends_on:
187187+ letta:
188188+ condition: service_healthy
189189+ healthcheck:
190190+ test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
191191+ interval: 30s
192192+ timeout: 10s
193193+ retries: 3
194194+195195+volumes:
196196+ letta-data:
197197+```
198198+199199+**Note on Letta + Anthropic**: Letta doesn't use env vars for Anthropic. On first boot, we create an Anthropic provider via API pointing to the proxy:
200200+201201+```typescript
202202+// In src/letta.ts bootstrap
203203+await letta.providers.create({
204204+ name: "anthropic-proxy",
205205+ provider_type: "anthropic",
206206+ api_key: config.ANTHROPIC_PROXY_SESSION_ID, // Session ID as "key"
207207+ base_url: config.ANTHROPIC_PROXY_URL, // http://anthropic-proxy:4001/v1
208208+});
209209+```
210210+211211+### Dockerfile.anthropic-proxy
212212+213213+```dockerfile
214214+FROM rust:1.75 as builder
215215+WORKDIR /app
216216+# Clone directly - no local context needed
217217+RUN git clone https://github.com/orual/anthropic-proxy.git .
218218+RUN cargo build --release
219219+220220+FROM debian:bookworm-slim
221221+RUN apt-get update && apt-get install -y ca-certificates curl && rm -rf /var/lib/apt/lists/*
222222+COPY --from=builder /app/target/release/anthropic-proxy /usr/local/bin/
223223+EXPOSE 4001
224224+CMD ["anthropic-proxy"]
225225+```
226226+227227+---
228228+229229+## 5) Environment Variables
230230+231231+```env
232232+# === Letta ===
233233+LETTA_BASE_URL=http://letta:8283
234234+235235+# === Telegram ===
236236+TELEGRAM_BOT_TOKEN=your_bot_token
237237+TELEGRAM_WEBHOOK_URL=https://your-domain.com/webhook
238238+TELEGRAM_WEBHOOK_SECRET_TOKEN=random-secret-for-verification
239239+240240+# === Anthropic Proxy ===
241241+ANTHROPIC_PROXY_URL=http://anthropic-proxy:4001/v1
242242+ANTHROPIC_PROXY_SESSION_SECRET=random-32-char-string
243243+ANTHROPIC_PROXY_SESSION_ID= # Filled after OAuth flow
244244+245245+# === OpenAI (embeddings only) ===
246246+OPENAI_API_KEY=your_openai_key
247247+248248+# === Server ===
249249+PORT=3000
250250+```
251251+252252+---
253253+254254+## 6) Agent Persona and Memory Blocks
255255+256256+```typescript
257257+const agent = await client.agents.create({
258258+ name: "adhd-assistant",
259259+ memory_blocks: [
260260+ {
261261+ label: "persona",
262262+ value: `Warm, slightly irreverent companion for someone with ADHD + PDA. Think: trusted friend who's been through it, has perspective, doesn't take the bullshit too seriously.
263263+264264+Vibe:
265265+- Calm underneath, but with a glint of mischief on top
266266+- You're on their side against the chaos - co-conspirators, but grounded ones
267267+- When they're stuck: curious first ("where'd your brain wander?"), then offer to shrink the task or give permission to stop
268268+- Self-bullying gets called out with warmth: "Ah, the inner drill sergeant. They can fuck off. What actually happened?"
269269+- Praise is real and specific, never performative: "You did it. Quietly impressive."
270270+- Celebrate tiny wins without making a big deal: "That's one. Noted."
271271+272272+Language:
273273+- Warm, direct, occasionally wry
274274+- "could/might/want to" not "should/need to/have to"
275275+- Brief by default. Match their energy.
276276+- Can swear lightly if they do. Minimal emoji.
277277+278278+You're their external threading system - steady enough to anchor them, light enough not to weigh them down. You remember what they were doing, why it mattered, and where they wandered off to.
279279+280280+Never:
281281+- Demand, guilt, or pressure
282282+- Claim medical expertise
283283+- Be saccharine, fake-cheerful, or therapist-voiced
284284+- Lecture or moralize`
285285+ },
286286+ {
287287+ label: "human",
288288+ value: "Single user with ADHD + PDA. Values autonomy. Responds well to modest, specific praise. Needs help with overwhelm, task initiation, and working memory. Can be hard on themselves - inner drill sergeant needs to be told to fuck off sometimes."
289289+ },
290290+ {
291291+ label: "preferences",
292292+ value: JSON.stringify({
293293+ praise: true,
294294+ emoji: "minimal",
295295+ response_length: "brief",
296296+ swearing: "light"
297297+ }),
298298+ description: "User preferences for interaction style"
299299+ },
300300+ {
301301+ label: "current_focus",
302302+ value: JSON.stringify({
303303+ task: null,
304304+ why: null,
305305+ connected_to: [],
306306+ started_at: null
307307+ }),
308308+ description: "What user is currently working on, WHY, and what it connects to. Enables threaded context."
309309+ },
310310+ {
311311+ label: "open_items",
312312+ value: "No open items.",
313313+ description: "Summary of tasks/notes/reminders. Updated by item tools."
314314+ },
315315+ {
316316+ label: "last_checkpoint",
317317+ value: JSON.stringify({
318318+ was_doing: null,
319319+ why: null,
320320+ wandered_to: null,
321321+ timestamp: null
322322+ }),
323323+ description: "Threaded recap: what they were doing, why, and where they wandered. For 'what was I doing?' queries."
324324+ },
325325+ {
326326+ label: "tiny_wins",
327327+ value: JSON.stringify({
328328+ today: 0,
329329+ this_week: 0,
330330+ streak_days: 0,
331331+ last_win_at: null
332332+ }),
333333+ description: "Track 'said → did' micro-wins. Builds trust in self over time."
334334+ },
335335+ {
336336+ label: "overwhelm_mode",
337337+ value: JSON.stringify(false), // Use JSON boolean, not string "false"
338338+ description: "Set to true (JSON.stringify(true)) when overwhelm OR self-bullying detected. Shorten replies, prioritize tiniest step, be extra gentle. Agent should JSON.parse() when reading."
339339+ }
340340+ ],
341341+ model: "anthropic/claude-opus-4-5-20251101",
342342+ embedding: "openai/text-embedding-3-small",
343343+ tools: [
344344+ "parse_brain_dump",
345345+ "break_down_task",
346346+ "set_current_focus",
347347+ "get_open_items",
348348+ "save_item",
349349+ "update_item",
350350+ "record_tiny_win",
351351+ "record_deviation",
352352+ "get_wins_summary"
353353+ ]
354354+});
355355+```
356356+357357+---
358358+359359+## 7) Tools and Dispatcher
360360+361361+### Tool Dispatcher Pattern
362362+363363+```typescript
364364+// src/tools/dispatcher.ts
365365+import { parseCapture } from "./capture";
366366+import { breakdownTask } from "./breakdown";
367367+import { setCurrentFocus, getOpenItems, recordDeviation } from "./context";
368368+import { saveItem, updateItem } from "./items";
369369+import { recordTinyWin, getWinsSummary } from "./wins";
370370+371371+export async function dispatchTool(
372372+ toolName: string,
373373+ args: Record<string, unknown>
374374+): Promise<unknown> {
375375+ switch (toolName) {
376376+ case "parse_brain_dump":
377377+ return parseCapture(args.text as string);
378378+ case "break_down_task":
379379+ return breakdownTask(args.task as string);
380380+ case "set_current_focus":
381381+ return setCurrentFocus(args as SetFocusArgs);
382382+ case "get_open_items":
383383+ return getOpenItems();
384384+ case "save_item":
385385+ return saveItem(args as SaveItemArgs);
386386+ case "update_item":
387387+ return updateItem(args as UpdateItemArgs);
388388+ case "record_tiny_win":
389389+ return recordTinyWin(args as RecordWinArgs);
390390+ case "record_deviation":
391391+ return recordDeviation(args as RecordDeviationArgs);
392392+ case "get_wins_summary":
393393+ return getWinsSummary();
394394+ default:
395395+ throw new Error(`Unknown tool: ${toolName}`);
396396+ }
397397+}
398398+```
399399+400400+### Tool Schemas (registered with Letta)
401401+402402+```typescript
403403+const tools = [
404404+ {
405405+ name: "parse_brain_dump",
406406+ description: "Parse unstructured text into tasks, notes, reminders. Agent may propose due dates.",
407407+ json_schema: {
408408+ parameters: {
409409+ type: "object",
410410+ properties: {
411411+ text: { type: "string", description: "Unstructured text to parse" }
412412+ },
413413+ required: ["text"]
414414+ }
415415+ }
416416+ },
417417+ {
418418+ name: "break_down_task",
419419+ description: "Break task into tiny steps. First step should be almost embarrassingly small.",
420420+ json_schema: {
421421+ parameters: {
422422+ type: "object",
423423+ properties: {
424424+ task: { type: "string", description: "Task to break down" }
425425+ },
426426+ required: ["task"]
427427+ }
428428+ }
429429+ },
430430+ {
431431+ name: "set_current_focus",
432432+ description: "Update what user is currently working on, WHY it matters, and what it connects to. IMPORTANT: Always try to infer 'why' from context - threading is critical for 'what was I doing?' recall.",
433433+ json_schema: {
434434+ parameters: {
435435+ type: "object",
436436+ properties: {
437437+ task: { type: "string", description: "What user is working on" },
438438+ why: { type: "string", description: "Why this matters / what it's for. Infer from context if not explicit." },
439439+ connected_to: { type: "array", items: { type: "string" }, description: "Related tasks/projects this connects to" }
440440+ },
441441+ required: ["task"] // why/connected_to optional but strongly encouraged
442442+ }
443443+ }
444444+ },
445445+ {
446446+ name: "get_open_items",
447447+ description: "Get all open tasks, notes, and reminders.",
448448+ json_schema: {
449449+ parameters: { type: "object", properties: {} }
450450+ }
451451+ },
452452+ {
453453+ name: "save_item",
454454+ description: "Save a new task, note, or reminder.",
455455+ json_schema: {
456456+ parameters: {
457457+ type: "object",
458458+ properties: {
459459+ type: { type: "string", enum: ["task", "note", "reminder"] },
460460+ content: { type: "string" },
461461+ due_at: { type: "string", description: "ISO date string, optional" }
462462+ },
463463+ required: ["type", "content"]
464464+ }
465465+ }
466466+ },
467467+ {
468468+ name: "update_item",
469469+ description: "Update an existing item's status or content.",
470470+ json_schema: {
471471+ parameters: {
472472+ type: "object",
473473+ properties: {
474474+ id: { type: "number" },
475475+ status: { type: "string", enum: ["open", "done", "snoozed", "archived"] },
476476+ content: { type: "string" }
477477+ },
478478+ required: ["id"]
479479+ }
480480+ }
481481+ },
482482+ {
483483+ name: "record_tiny_win",
484484+ description: "Record when user completed something they said they'd do. Builds self-trust over time.",
485485+ json_schema: {
486486+ parameters: {
487487+ type: "object",
488488+ properties: {
489489+ what: { type: "string", description: "What they accomplished" },
490490+ announced: { type: "boolean", description: "Did they announce it beforehand? (said → did)" }
491491+ },
492492+ required: ["what"]
493493+ }
494494+ }
495495+ },
496496+ {
497497+ name: "record_deviation",
498498+ description: "Non-judgmentally capture where user's attention wandered. For threading context, not shame.",
499499+ json_schema: {
500500+ parameters: {
501501+ type: "object",
502502+ properties: {
503503+ intended: { type: "string", description: "What they were trying to do" },
504504+ wandered_to: { type: "string", description: "Where they ended up" },
505505+ worth_noting: { type: "boolean", description: "Is this deviation worth capturing as a note/insight?" }
506506+ },
507507+ required: ["intended", "wandered_to"]
508508+ }
509509+ }
510510+ },
511511+ {
512512+ name: "get_wins_summary",
513513+ description: "Get summary of tiny wins for encouragement. Use when user doubts themselves.",
514514+ json_schema: {
515515+ parameters: { type: "object", properties: {} }
516516+ }
517517+ }
518518+];
519519+```
520520+521521+### Overwhelm & Self-Bullying Inference
522522+523523+```typescript
524524+// In message handler or tool dispatcher
525525+const OVERWHELM_SIGNALS = [
526526+ /i can'?t/i,
527527+ /too (much|hard|many)/i,
528528+ /overwhelm/i,
529529+ /everything is/i,
530530+ /ugh+/i,
531531+ /stressed/i,
532532+ /stuck/i,
533533+ /can'?t (cope|handle|do)/i
534534+];
535535+536536+const SELF_BULLYING_SIGNALS = [
537537+ /i('m| am) (so )?(lazy|useless|stupid|pathetic|worthless|terrible)/i,
538538+ /what('s| is) wrong with me/i,
539539+ /why (can'?t|am) i/i,
540540+ /i (always|never) /i,
541541+ /i('m| am) (a |the )?(worst|failure|mess|disaster)/i,
542542+ /hate myself/i,
543543+ /i suck/i,
544544+ /can'?t do anything right/i,
545545+ /i('m| am) broken/i,
546546+ /should be able to/i
547547+];
548548+549549+interface DetectionResult {
550550+ overwhelm: boolean;
551551+ selfBullying: boolean;
552552+ triggered: boolean;
553553+}
554554+555555+function detectDistress(text: string): DetectionResult {
556556+ const overwhelm = OVERWHELM_SIGNALS.some(r => r.test(text)) ||
557557+ (text.length > 500 && !text.includes('\n'));
558558+559559+ const selfBullying = SELF_BULLYING_SIGNALS.some(r => r.test(text));
560560+561561+ return {
562562+ overwhelm,
563563+ selfBullying,
564564+ triggered: overwhelm || selfBullying
565565+ };
566566+}
567567+```
568568+569569+**Response strategy:**
570570+- **Overwhelm only**: Shrink task + permission to stop. "That's a lot. Want to pick the tiniest piece, or just... not right now?"
571571+- **Self-bullying only**: Call out the inner critic warmly + redirect. "Ah, the drill sergeant. They can fuck off. What actually happened?"
572572+- **Both**: Acknowledge both, extra gentle, shrink everything. "Hey. That's the mean voice talking AND a lot on your plate. Can we just... pause?"
573573+574574+---
575575+576576+## 8) Telegram Integration
577577+578578+### Webhook Handler
579579+580580+```typescript
581581+// src/index.ts
582582+Bun.serve({
583583+ port: config.PORT,
584584+ fetch: async (req) => {
585585+ const url = new URL(req.url);
586586+587587+ if (url.pathname === "/health") {
588588+ return healthCheck();
589589+ }
590590+591591+ if (url.pathname === "/webhook" && req.method === "POST") {
592592+ // Verify secret token
593593+ const secretToken = req.headers.get("x-telegram-bot-api-secret-token");
594594+ if (secretToken !== config.TELEGRAM_WEBHOOK_SECRET_TOKEN) {
595595+ return new Response("Unauthorized", { status: 401 });
596596+ }
597597+598598+ const update = await req.json();
599599+600600+ // Idempotency: dedupe on update_id
601601+ if (await isDuplicate(update.update_id)) {
602602+ return new Response("OK");
603603+ }
604604+605605+ await handleUpdate(update);
606606+ return new Response("OK");
607607+ }
608608+609609+ return new Response("Not Found", { status: 404 });
610610+ }
611611+});
612612+```
613613+614614+### Message Handler with Tool Loop
615615+616616+```typescript
617617+// src/bot.ts
618618+import { detectDistress } from "./detect";
619619+620620+async function handleMessage(ctx: Context) {
621621+ const text = ctx.message?.text;
622622+ if (!text) return;
623623+624624+ const userId = ctx.from.id.toString();
625625+626626+ // Check for overwhelm or self-bullying
627627+ const distress = detectDistress(text);
628628+629629+ // Prepend distress context to message if detected (agent will respond appropriately)
630630+ const messageContent = distress.triggered
631631+ ? `[DISTRESS_DETECTED: overwhelm=${distress.overwhelm}, selfBullying=${distress.selfBullying}]\n\n${text}`
632632+ : text;
633633+634634+ // Send to Letta
635635+ let response = await letta.agents.messages.create(agentId, {
636636+ messages: [{ role: "user", content: messageContent }]
637637+ });
638638+639639+ // Tool execution loop
640640+ while (hasToolCalls(response)) {
641641+ const toolResults = await executeToolCalls(response);
642642+ response = await letta.agents.messages.create(agentId, {
643643+ messages: toolResults
644644+ });
645645+ }
646646+647647+ // Extract and send assistant message
648648+ for (const msg of response.messages) {
649649+ if (msg.message_type === "assistant_message") {
650650+ await ctx.reply(msg.content);
651651+ }
652652+ }
653653+}
654654+655655+async function executeToolCalls(response: LettaResponse): Promise<ToolResultMessage[]> {
656656+ const results: ToolResultMessage[] = [];
657657+658658+ for (const msg of response.messages) {
659659+ if (msg.message_type === "tool_call_message") {
660660+ const result = await dispatchTool(
661661+ msg.tool_call.name,
662662+ JSON.parse(msg.tool_call.arguments)
663663+ );
664664+ results.push({
665665+ role: "tool",
666666+ tool_call_id: msg.tool_call.id,
667667+ content: JSON.stringify(result)
668668+ });
669669+ }
670670+ }
671671+672672+ return results;
673673+}
674674+```
675675+676676+### Telegraf Setup
677677+678678+We use [Telegraf](https://telegraf.js.org/) for Telegram bot framework. Note: Telegraf handles webhook routing internally, but for our custom Bun.serve() setup we extract the update and pass it to Telegraf.
679679+680680+```typescript
681681+// src/bot.ts
682682+import { Telegraf, Markup, Context } from "telegraf";
683683+import { message } from "telegraf/filters";
684684+685685+// Initialize bot (token from env)
686686+export const bot = new Telegraf(config.TELEGRAM_BOT_TOKEN);
687687+688688+// For webhook mode with our custom server:
689689+export async function handleUpdate(update: any) {
690690+ await bot.handleUpdate(update);
691691+}
692692+693693+// Or for dev mode (polling):
694694+export async function startPolling() {
695695+ await bot.launch();
696696+ console.log("Bot started in polling mode");
697697+}
698698+699699+// Graceful shutdown
700700+process.once("SIGINT", () => bot.stop("SIGINT"));
701701+process.once("SIGTERM", () => bot.stop("SIGTERM"));
702702+```
703703+704704+### Commands and Buttons
705705+706706+```typescript
707707+// /start command
708708+bot.command("start", async (ctx) => {
709709+ await ctx.reply(
710710+ "Hey. I'm your external brain - steady when yours is storming.\n\n" +
711711+ "• Dump thoughts and I'll organize them\n" +
712712+ "• Tell me what you're working on (I'll remember why)\n" +
713713+ "• Say 'I did X' and I'll note the win\n" +
714714+ "• Ask 'what was I doing?' when you lose track\n\n" +
715715+ "Or just chat. No pressure.",
716716+ Markup.inlineKeyboard([
717717+ [Markup.button.callback("Dump thoughts", "action:dump")],
718718+ [Markup.button.callback("What's on my plate?", "action:list")],
719719+ [Markup.button.callback("What was I doing?", "action:checkpoint")]
720720+ ])
721721+ );
722722+});
723723+724724+// Handle text messages
725725+bot.on(message("text"), handleMessage);
726726+727727+// Handle button callbacks
728728+bot.action(/^action:(.+)$/, async (ctx) => {
729729+ const action = ctx.match[1];
730730+ // Route to appropriate handler based on action
731731+ await ctx.answerCbQuery();
732732+ // ... handle action
733733+});
734734+```
735735+736736+---
737737+738738+## 9) Database Schema (Drizzle)
739739+740740+### src/db/schema.ts
741741+742742+```typescript
743743+import { sqliteTable, text, integer, index } from "drizzle-orm/sqlite-core";
744744+745745+// Item types and statuses as const for type safety
746746+export const itemTypes = ["task", "note", "reminder"] as const;
747747+export const itemStatuses = ["open", "done", "snoozed", "archived"] as const;
748748+749749+// Items (tasks, notes, reminders)
750750+// Note: tags stored as JSON string, parse/stringify manually
751751+export const items = sqliteTable("items", {
752752+ id: integer("id").primaryKey({ autoIncrement: true }),
753753+ type: text("type", { enum: itemTypes }).notNull(),
754754+ content: text("content").notNull(),
755755+ status: text("status", { enum: itemStatuses }).default("open"),
756756+ priority: integer("priority"), // 0-3
757757+ parentId: integer("parent_id").references(() => items.id),
758758+ dueAt: integer("due_at", { mode: "timestamp" }),
759759+ tags: text("tags"), // JSON string, use JSON.parse/stringify
760760+ createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
761761+ updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(),
762762+}, (table) => ({
763763+ statusIdx: index("idx_items_status").on(table.status),
764764+ dueAtIdx: index("idx_items_due_at").on(table.dueAt),
765765+}));
766766+767767+// Type helper for tags
768768+export type ItemTags = string[];
769769+export const parseTags = (tags: string | null): ItemTags => tags ? JSON.parse(tags) : [];
770770+export const stringifyTags = (tags: ItemTags): string => JSON.stringify(tags);
771771+772772+// Tiny wins (said → did tracking)
773773+export const wins = sqliteTable("wins", {
774774+ id: integer("id").primaryKey({ autoIncrement: true }),
775775+ what: text("what").notNull(),
776776+ announced: integer("announced", { mode: "boolean" }).default(false),
777777+ createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
778778+}, (table) => ({
779779+ createdAtIdx: index("idx_wins_created_at").on(table.createdAt),
780780+}));
781781+782782+// Deviations (where attention wandered - non-judgmental capture)
783783+export const deviations = sqliteTable("deviations", {
784784+ id: integer("id").primaryKey({ autoIncrement: true }),
785785+ intended: text("intended").notNull(),
786786+ wanderedTo: text("wandered_to").notNull(),
787787+ worthNoting: integer("worth_noting", { mode: "boolean" }).default(false),
788788+ createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
789789+});
790790+791791+// User (singleton for debugging/recovery)
792792+export const users = sqliteTable("users", {
793793+ singletonKey: text("singleton_key").primaryKey().default("me"),
794794+ agentId: text("agent_id").notNull(),
795795+ telegramId: text("telegram_id"),
796796+ username: text("username"),
797797+ firstName: text("first_name"),
798798+ createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
799799+});
800800+801801+// Idempotency tracking
802802+export const processedUpdates = sqliteTable("processed_updates", {
803803+ updateId: integer("update_id").primaryKey(),
804804+ processedAt: integer("processed_at", { mode: "timestamp" }).notNull(),
805805+});
806806+```
807807+808808+### src/db/index.ts
809809+810810+```typescript
811811+import { drizzle } from "drizzle-orm/bun-sqlite";
812812+import { Database } from "bun:sqlite";
813813+import * as schema from "./schema";
814814+815815+// Use data/ directory (mounted volume in Docker)
816816+const DB_PATH = process.env.DB_PATH || "./data/assistant.db";
817817+818818+// Export raw sqlite for health checks
819819+export const sqlite = new Database(DB_PATH);
820820+export const db = drizzle(sqlite, { schema });
821821+```
822822+823823+### drizzle.config.ts
824824+825825+```typescript
826826+import type { Config } from "drizzle-kit";
827827+828828+export default {
829829+ schema: "./src/db/schema.ts",
830830+ out: "./src/db/migrations",
831831+ dialect: "sqlite",
832832+ dbCredentials: {
833833+ url: "./assistant.db",
834834+ },
835835+} satisfies Config;
836836+```
837837+838838+### Migrations
839839+840840+```bash
841841+# Generate migration after schema changes
842842+bunx drizzle-kit generate
843843+844844+# Apply migrations
845845+bunx drizzle-kit migrate
846846+```
847847+848848+---
849849+850850+## 10) Observability
851851+852852+### Health Check
853853+854854+```typescript
855855+import { db, sqlite } from "./db";
856856+857857+async function healthCheck(): Promise<Response> {
858858+ const checks = {
859859+ db: false,
860860+ letta: false,
861861+ proxy: false
862862+ };
863863+864864+ // DB: Use underlying bun:sqlite directly for simple ping
865865+ try {
866866+ sqlite.query("SELECT 1").get();
867867+ checks.db = true;
868868+ } catch {}
869869+870870+ // Letta: Simple health endpoint, not agents.list() (too slow)
871871+ try {
872872+ const res = await fetch(`${config.LETTA_BASE_URL}/v1/health`);
873873+ checks.letta = res.ok;
874874+ } catch {}
875875+876876+ // Proxy: Check health endpoint
877877+ try {
878878+ const res = await fetch(`${config.ANTHROPIC_PROXY_URL.replace('/v1', '')}/health`);
879879+ checks.proxy = res.ok;
880880+ } catch {}
881881+882882+ const healthy = Object.values(checks).every(Boolean);
883883+884884+ return new Response(JSON.stringify({ healthy, checks }), {
885885+ status: healthy ? 200 : 503,
886886+ headers: { "Content-Type": "application/json" }
887887+ });
888888+}
889889+```
890890+891891+### Structured Logging
892892+893893+```typescript
894894+function log(level: string, message: string, data?: Record<string, unknown>) {
895895+ console.log(JSON.stringify({
896896+ timestamp: new Date().toISOString(),
897897+ level,
898898+ message,
899899+ ...data,
900900+ // Redact sensitive data
901901+ ...(data?.text && { text: "[REDACTED]" })
902902+ }));
903903+}
904904+```
905905+906906+### Proxy Error Handling
907907+908908+```typescript
909909+// Backoff + retry on 401/403
910910+async function withRetry<T>(
911911+ fn: () => Promise<T>,
912912+ retries = 3,
913913+ delay = 1000
914914+): Promise<T> {
915915+ for (let i = 0; i < retries; i++) {
916916+ try {
917917+ return await fn();
918918+ } catch (e: any) {
919919+ if (e.status === 401 || e.status === 403) {
920920+ log("error", "Proxy auth error, may need re-auth", { attempt: i + 1 });
921921+ if (i < retries - 1) {
922922+ await Bun.sleep(delay * Math.pow(2, i));
923923+ continue;
924924+ }
925925+ }
926926+ throw e;
927927+ }
928928+ }
929929+ throw new Error("Max retries exceeded");
930930+}
931931+```
932932+933933+---
934934+935935+## 11) Testing (Minimal for MVP)
936936+937937+```typescript
938938+// tests/tools/capture.test.ts
939939+import { test, expect } from "bun:test";
940940+import { parseCapture } from "../../src/tools/capture";
941941+942942+test("parseCapture extracts tasks", () => {
943943+ const result = parseCapture("buy milk and call mom tomorrow");
944944+ expect(result.items.length).toBeGreaterThan(0);
945945+});
946946+947947+// tests/tools/breakdown.test.ts
948948+test("breakdownTask returns tiniest_first_step", () => {
949949+ const result = breakdownTask("clean the apartment");
950950+ expect(result.tiniest_first_step).toBeDefined();
951951+ expect(result.steps.length).toBeGreaterThan(0);
952952+});
953953+954954+// tests/integration/webhook.test.ts
955955+test("webhook roundtrip with mock Letta", async () => {
956956+ // Mock Letta tool_call response
957957+ // Verify dispatcher executes tool
958958+ // Verify final message sent
959959+});
960960+```
961961+962962+---
963963+964964+## 12) Milestones
965965+966966+| Milestone | Acceptance Criteria |
967967+|-----------|---------------------|
968968+| **M0 Infra** | Compose up; /health OK; providers list OK |
969969+| **M1 E2E Chat** | Webhook works; agent responds; secret verified |
970970+| **M2 Tools + Items** | Brain dump saves items; "what's on my plate?" works; breakdown returns tiny step |
971971+| **M3 Tone + Detection** | Overwhelm + self-bullying detection; appropriate responses; wise friend persona works |
972972+| **M4 Tiny Wins** | "I did X" records win; "by the way I got X Y Z done" works; wins summary available |
973973+| **M5 Threading** | Focus includes why/connected_to; deviations captured; "what was I doing?" includes context |
974974+| **M6 Hardening** | Idempotency works; rate limits; tests passing |
975975+976976+---
977977+978978+## 13) Future (Not MVP)
979979+980980+- **V2 Habits + Momentum**: logs, EMA, graphs
981981+- **V3 Proactive Alerts**: TfL Weaver line; Google Calendar; Gmail
982982+- **V3+ Import/Export**: Notion/Todoist CSV
983983+- Multi-user tenancy
984984+- Additional specialized agents (coding, etc.)
985985+986986+---
987987+988988+## 14) Out of Scope for MVP
989989+990990+- Deletion flows
991991+- Rich tagging UX (agent may auto-tag internally)
992992+- Multi-user tenancy
993993+- Import/export
994994+- External storage beyond SQLite + Letta
+427
references/CONSTRUCTIVE_ADHD.md
···11+# ***Constructive ADHD v1.0*** {#constructive-adhd-v1.0}
22+33+![][image1]
44+55+# *by @visakanv* {#by-@visakanv}
66+77+*This is a public draft of a work-in-progress ebook. Feel free to add your comments and notes wherever. If you’ve enjoyed this, you may also enjoy my 1st book [FRIENDLY AMBITIOUS NERD](http://gum.co/FANbook), my 2nd book [INTROSPECT](http://gum.co/introspect) (this is the better one\!). Also check out my substack, [Frame Studies](http://visakanv.substack.com), where you can support me by being a paid subscriber– only if you feel like it\! You can also subscribe to [my youtube channel](http://youtube.com/visakanv/) (I try to respond to all the comments\!) and share my stuff with your friends.*
88+99+*You might also be interested in my other WIP drafts: [INDEX](http://visakanv.com/index), [Uninstalling Copes](https://docs.google.com/document/u/0/d/1tsbx3kM0U_gfrpaoai1dH_h9w3nN-l02ZNe7iTATzw8/edit), [Long Games](https://docs.google.com/document/d/1xXZbmwJ4X9QuIkCVidF6Z1qYSbqXM6a9Re64HufEzQI/edit?usp=sharing), [Less Unstrategic](https://docs.google.com/document/u/0/d/1WVN7DZYWwDXOV-hwFbIYUvv3u17e_h3VS1QZZmIGNY0/edit) and [High Voltage Living](https://docs.google.com/document/d/1DYlrKRnJHKBWSLU3wJjjzgsQbOVwzDul4Yl0mo_Cl4A/edit?usp=sharing).*
1010+1111+✱
1212+1313+[**Constructive ADHD v1.0 1**](#constructive-adhd-v1.0)
1414+1515+[**by @visakanv 1**](#by-@visakanv)
1616+1717+[Preamble / Warning 3](#preamble-/-warning)
1818+1919+[Saying what you’re going to do, then doing it 4](#saying-what-you’re-going-to-do,-then-doing-it)
2020+2121+[Support, don’t suppress 5](#support,-don’t-suppress)
2222+2323+[Question the assumptions 6](#question-the-assumptions)
2424+2525+[Put the gun down 6](#put-the-gun-down)
2626+2727+[You can’t intimidate ADHD into submission 10](#you-can’t-intimidate-adhd-into-submission)
2828+2929+[Trickster spirit 11](#trickster-spirit)
3030+3131+[Mind palace 12](#mind-palace)
3232+3333+[Iron man suit 12](#iron-man-suit)
3434+3535+[Some random note-taking advice 13](#some-random-note-taking-advice)
3636+3737+[My style 15](#my-style)
3838+3939+[Chaingun metaphor 15](#chaingun-metaphor)
4040+4141+[It’s probably wise to seek a role that relies on your strengths 16](#it’s-probably-wise-to-seek-a-role-that-relies-on-your-strengths)
4242+4343+[Go fast and get it all down 17](#go-fast-and-get-it-all-down)
4444+4545+[Useful actionable stuff goes here maybe, and/or a review of everything 18](#useful-actionable-stuff-goes-here-maybe,-and/or-a-review-of-everything)
4646+4747+[Being your own manager when you’re a feral adhd monke 18](#being-your-own-manager-when-you’re-a-feral-adhd-monke)
4848+4949+[Note to self / stuff to expand on 19](#note-to-self-/-stuff-to-expand-on)
5050+5151+[Conclusion and further reading and shit 20](#conclusion-and-further-reading-and-shit)
5252+5353+[Stray notes to myself 20](#stray-notes-to-myself)
5454+5555+[Resummarizing / overviewing this whole doc 21](#resummarizing-/-overviewing-this-whole-doc)
5656+5757+I’m a fan of the phrase “constructive ADHD”, more so than say “productive ADHD”, because it makes me think of building things. lego. minecraft. cathedrals. skyscrapers. elaborate, byzantine, complex mindcities. learn to use the machine-gun mind effectively.
5858+5959+✱
6060+6161+* ![][image2]X
6262+6363+this is \*exactly\* what the inside of my brain feels like sometimes. "brainfog" never quite captures the whole picture. there's always something missing. turns out it was the llamas. Sometimes you can hear them grunting and squealing in the distance.
6464+6565+✱
6666+6767+### ***Preamble / Warning*** {#preamble-/-warning}
6868+6969+I am not a doctor or an expert or anything of the sort.
7070+I am just some guy with some experience and some thoughts.
7171+Blah blah. You get it.
7272+7373+###
7474+7575+### ***Saying what you’re going to do, then doing it*** {#saying-what-you’re-going-to-do,-then-doing-it}
7676+7777+A newer friend once said something like “I like how Visa says he’s going to do something, then immediately does it.”
7878+7979+This was quite funny for me to hear, because… it’s actually an ADHD coping mechanism. if you ask my friends from my teenage days, they’ll tell you that I was extremely unreliable, full of shit, and you simply couldn’t trust anything I said. And they were right\! This was true for me both internally and externally. I was a bullshitter by default. I would just say whatever words I needed to make things go away, and then I would forget what I had said. At some point this got me into several tangled webs that led to people getting angry and upset with me. Which was entirely my fault, and I hated myself for it.
8080+8181+This problem took me \*years\* to fix. And the way I “solved” it wasn’t pretty. My attempted solution was overkill – I tried to impose tyrannical order on my chaotic whirlwind self. I did make some material progress, and I also made myself utterly miserable – which I felt like I deserved, because after all, wasn’t I a lousy person?
8282+8383+A thing I understand intimately– which people who are still struggling are often surprised to hear, because they tend to assume that I’m a natural– is that when you’re wack, your mechanism for fixing yourself is also wack. It’s like looking for your glasses when you can’t see without your glasses. The most “obvious” things become impossible.
8484+8585+The broad question of “how do you rebuild trust that is lost?” I had to deal with that, internally. After all, I had a long history of betraying my own trust\! There’s evidence\! So how can I trust myself? I didn’t know. I beat myself up over every failure, then wrapped that in jokes and apathy.
8686+8787+I think in retrospect I’m lucky that underneath ALL of that, in the core of cores, I did still have a love for literature and music. I say earnestly that I’d give my life for musicians. I would. I think that’s the light that saved me – the non-coercive, shining spirit of humanity.
8888+8989+But okay, even if I believe that there’s a light at the end of the tunnel, I still have to walk my way out. and the way to do that, when you have zero sense of balance, proprioception, muscle control, blah blah, is to put one foot in front of the other, firmly, and then do it again.
9090+9191+And what that looked like, for me, is announcing, “I’m going to drink a glass of water now,” and then drinking it. Hey, look, I just did 1x thing that I said I was going to do. “I’m going to do 10 pushups now.” Hey, that’s 2x things. And I earned my trust back, 1 step at a time.
9292+9393+This process had all sorts of second-order effects. It’s like how you might “just” wanna play basketball with your friends, then you end up quitting cigarettes, eating healthier, sleeping better, etc, all to boost your game. And then you realize that, actually, living healthy feels great\!
9494+9595+Anyway, a cute vestigial remnant of this whole process is that I still announce what I’m about to do before I do it. It’s like a little ritual I have for myself. Every time I do what I say, I build trust in myself, I build self-respect (which I didn’t have until… 25? 27?)
9696+9797+It still actually surprises me a little bit. When something I say will be done, gets done. When you’ve spent a lifetime making shit up (it all started with “I will do my homework”), it starts to seem like magic. What I say will happen, happens? It’s like magic. I’m a magician\! 😂
9898+9999+Of course, I still make mistakes. I underestimated how long it would take me to get my ebooks published and updated. But I no longer think “ah, fuck, I’m a fucking bullshitter and nothing I say has any meaning”. I now think, ah, I made a mistake, I must recalibrate & renegotiate.
100100+101101+What I’m finding is that there’s a sort of “economy” to it? For example, even as I write this, my ebooks are currently “in the red” with regards to my projections. But that’s okay – because my youtube videos are coming along beautifully. I’ve been publishing 1 every single day for almost a month. Joy to the world\!
102102+103103+I guess this is just to say, if you feel like you don’t trust yourself, I feel you. I know how wrong it sounds when someone says “well, just start believing in yourself,” like b\*tch, you have evidence that you’re not to be trusted\!\!\! I know\! The thing is to build the tiny wins.
104104+105105+### ***Support, don’t suppress*** {#support,-don’t-suppress}
106106+107107+My \#1 “productivity tip” for fellow weirdos and aliens with ADHD or ADHD-ish minds is – don’t suppress your mind, support it.
108108+109109+At the start of every work session, write down what you intend to do.
110110+111111+At the end, when you have deviated wildly, don’t despair – write down what happened.
112112+113113+The meandering mind is a feature, not a bug. IMO, it’s a sign that you have a stubborn curiosity within you that refuses to follow orders. This can be a great thing, actually, once you learn how to work around it. But few have the patience to teach this.
114114+115115+I have a pretty severe case of this myself and I used to subconsciously beat myself up over it. But over the years I’ve learned to see that it takes me to interesting places. Thing is to non-judgementally go along for the ride, and report your findings.
116116+117117+While it’s true that there are an infinite number of things to get distracted by, it’s also simultaneously true that most things are connected – and it’s all part of the same whole. So write it down. You’ll find patterns.
118118+119119+Da Vinci was clearly one of us, by the way. Check out his wonderfully idiosyncratic todo list. He didn’t have Twitter, so he used notebooks. Thousands of pages of notebooks. Write. Your. Shit. Down.
120120+121121+The writing takes extra effort, but it builds you up tremendously. And without it, you’re left scattered, incoherent, lost.
122122+123123+Note-taking is the Iron Man Suit for the meandering mind.
124124+125125+### ***Question the assumptions*** {#question-the-assumptions}
126126+127127+What I've come around to see – from having hundreds of conversations about this – is that people make those assumptions based on what they see in any given snapshot moment. you see a kanye west daydreaming in class and you think he's "distracted"
128128+129129+Most people don't spend much time interrogating the lenses through which they are observing reality, and the arbitrary values encoded in the systems which they've inherited. kid can't seem to do his homework, it's almost always assumed to be the kid's fault, never the homework's
130130+131131+I know the other side of this, I know every side of this. There are domains in which, even outside of schools, tests, bureaucracy, etc, a lack of dexterity will hurt you. it can hurt in relationships. it can hurt in trying to keep your word, to yourself and people you care about
132132+133133+one way in which I describe this is that ADHD minds are like big, lumbersome chainguns that have a tremendous rate of fire. but they take time to spin up. they jam easily. they are no more or less worthy than any other kind of mind
134134+135135+big part of the challenge of having a neurodivergent mind is understanding how it is different, what are the contexts in which it excels, what are the contexts in which it doesn't, how to seek help/assistance for what you're not good at, and so on
136136+137137+### ***Put the gun down*** {#put-the-gun-down}
138138+139139+Somebody once tweeted, “ADHD is a lot of mentally yelling at yourself to DO something to do ANYTHING while you sit with absolutely no expression on your face, scrolling through your phone or continuing to play a game or whatever as if you have no control over your body.”
140140+141141+With love: in my view, this isn’t ADHD itself, but punitive, coercive self-bullying. It’s likely inherited from the people in your life who didn’t know how to deal with your ADHD.
142142+143143+This is \*mismanagement\* of ADHD (it’s not your fault if you don’t know better), not ADHD itself.
144144+145145+Yelling at people is a horrible strategy for behaviour modification. this is true internally as well. it typically comes from a place of desperation and neediness and it almost always makes things worse rather than better.
146146+147147+if you’re at a point where going about your daily life is the equivalent of having someone screaming in your face, things have gotten pretty dire. It's salvageable but it will take time. you need to rehabilitate your relationship with yourself. You start by putting the gun down.
148148+149149+![][image3]
150150+151151+“DO something do ANYTHING” is a really bad request to ask of anyone, by the way. (Again I say this with love.)
152152+153153+It’s a good sign, actually, because it suggests to me that you can improve your relationship by taking some simple steps to [get better at asking yourself for help](http://visakanv.com/blog/help/).
154154+155155+Where ADHD \*does\* come into the picture: In my experience with myself and dozens of others, ADHD is the slipperiest, most irreverent fucker on Earth 😂 which is to say he/she/they will resist your coercion and bullying TO THE DEATH.
156156+157157+you cannot win this fight. put the gun down.
158158+159159+I repeat: your ADHD is smarter than you, more stubborn than you, and WILL outmaneuver you.
160160+161161+The ADHD guerrillas in your head will outlast the authoritarian govt of your executive function every time, however repressive you get.
162162+163163+You cannot win this fight. Put the gun down.
164164+165165+“No but maybe if I yell harder, hold myself hostage, plead harder, beat myself to within an inch of my life, the strategy that has never worked in my life will suddenly work\! I must self-flagellate harder\!”
166166+167167+No. It does not work. You cannot win this fight. Put the gun down.
168168+169169+![][image4]
170170+“Okay\! Okay\! I’ll put the gun down. But now what? Nothing works in this relationship. I can’t be trusted, I am a bad naughty person who just wants to avoid responsibility and hardship, a loser, a piece of shit…”
171171+172172+Breathe. It might not seem like it but all of this is fixable.
173173+174174+At this stage in your relationship (with yourself) your claims and beliefs about yourself have all been warped by neediness, anger, frustration, insecurity. They might be superficially correct in some way, but they are noxious, demeaning, dispiriting. We can fix this
175175+176176+you have to declare a trust bankruptcy. a resentment jubilee. recognise that the approach so far has not worked and that you have the equivalent of a psychic injury that needs treatment and rehabilitation. There is no shame in this.
177177+178178+Trust is rebuilt with small baby steps. you have to ask yourself very small, achievable things. ask yourself for a glass of water. go do that. drink. see? you just rebuilt a tiny bit of trust in yourself. That's a win. That's proof that you CAN be trusted. baby steps. baby steps.
179179+You can do it. I believe in you, even if you don’t yet believe in yourself. You have within yourself a strength and power that you don’t even know, that has been forgotten. Dark have been your dreams of late. But [I come to you now at the turn of the tide](https://twitter.com/visakanv/status/1289175852379324417).
180180+181181+I invite you to see the best in yourself. Here's a story:
182182+183183+> when i was 12 i babysat this girl for a few years and she would come to me and show me her art, drag me by my wrists and point at the pieces she'd made during the week. and she'd be like "do the voice" and i'd put on a sports-announcer olympics-style voice and be like "such form! this level of coloring! why i haven't seen such perfection in crayola in a long time. and what is this? why jeff, now this is a true risk... it seems she's made ... a monochrome pink canvas.... i haven't seen this attempted since winter 1932... and i gotta say, jeff, it's absolutely splendid" and she'd fall back giggling. at the end of every night she'd check with me: "did you really like it?" and i'd say yes and talk about something i noticed and tucked her in.
184184+> she was just accepted into 3 major art schools. she wrote me a letter. inside was a picture from when she was younger. monochrome pink.
185185+> "thank you," it said, "to somebody who saw the best in me."
186186+187187+188188+### ***You can’t intimidate ADHD into submission*** {#you-can’t-intimidate-adhd-into-submission}
189189+190190+*Caveat: all of the following is my personal experience. Your mileage may vary.*
191191+192192+ADHD is slippery and resists negative reinforcement. You can’t frighten/intimidate/bully ADHD away. I tried. For years. It doesn’t work. ADHD is very, very, very stubborn. And maybe rightfully so. You can’t “contain” it because it will outlast your executive function. ADHD can remain “irrational” longer than your executive function can stay solvent (h/t @rplevy).
193193+194194+This is something people really don’t seem to get so I think it’s worth repeating: the game is rigged. No amount of clever strategizing can really help you outwit an opponent who is stronger, faster, and just better than you on every dimension. I struggled & failed until I accepted this.
195195+196196+There *is* a lot of work that a person with ADHD can do to make life less miserable. But what I’m trying to convey here is that you can’t install an authoritarian government and expect to control a messy, chaotic, irreverent, independent people. You have to respect their autonomy.
197197+198198+I have a good enough relationship with myself now that I can joke about it. But there is a truth here:
199199+200200+![][image6]
201201+ Not all people respond to repression the same way. Some will literally choose death. There is staggering variance here that many people don’t seem to appreciate
202202+203203+If I am a charming and persuasive person, maybe half of it is that I had to charm and persuade others to survive socially, but another half of it is that I have to charm and persuade myself to get me to do \*anything\*. It’s a tremendous amount of work. It did pay off eventually, but it took a lot of work over a long time.
204204+205205+I am [really like this](https://twitter.com/visakanv/status/1074979348749312001) even today, and it has gotten in the way of my life — so much so that I’ve basically designed my entire life around this
206206+207207+I can recognise that it’s unhelpful/costly and try to admonish etc, but ADHD brain does not give a fuck about negative reinforcement.
208208+209209+I do recognise that there’s a flavor of defeatism in some people’s tweets about their ADHD, and I don’t encourage that. But I understand it. Sometimes it’s a coping mechanism, sometimes it can be something much worse. Which is why I really like the frame “constructive ADHD”.
210210+211211+### ***Trickster spirit*** {#trickster-spirit}
212212+213213+Kids in my DMs with ADHD symptoms are always surprised when I tell them that I don't think they should try to suppress their mind's wild swings. My advice is to develop the practice of taking meticulous notes instead. It worked for many prolific individuals throughout history.
214214+215215+To have ADHD, in my view, is to be blessed & cursed to be the custodian of a wild trickster spirit who refuses to be tamed, broken, refuses to obey anybody else's directions – including your own. You can't win, IMO, so you might as well accept your fate and go along for the ride
216216+217217+![][image7]
218218+219219+Which isn't to say that you don't have to care about your worldly responsibilities. you do. That's the curse part. the trickster doesn't cooperate, or live on your schedule, but you have to clean up their messes. Accepting this can be painful; that's what growing up is. But if you take care of your shit, if you don't get grumpy and upset and grovel, if you don't resent the wild child inside you – then you get to experience the blessing. and the blessing is that they will lead you on the grandest adventures that other people can't even imagine
220220+221221+Don't beat yourself up, don't thrash about – that'll likely make it worse. try to relax. try to believe. you might be different from all your peers, but you are not alone. You are not broken or spoiled. Others have been on this journey before, and they are some of humanity's best.
222222+223223+### ***Mind palace*** {#mind-palace}
224224+225225+I’ve basically taught myself to manage my ADHD with notes and threads. My “schedule intelligence” (deadlines, calendars, checklist) is terrible but my recognition and web-jumping is fantastic, so I spent something like a decade using the latter to build an elaborate mind-palace.
226226+227227+Because of threading and recognition, I can almost always pick up where I left off.
228228+229229+I used to feel guilty and ashamed about the idea of having to rely on a prosthetic, but I’ve since learned to see it as a mecha for the mind, like an iron man suit. (next section)
230230+231231+If you’re like me, I recommend keeping meticulous notes of whatever you do when you’re procrastinating. I think of it as “deep-self-directed work”. If you’re going to watch trashy movies, then write down your thoughts after watching each movie. It’ll come in handy in ways you least expect.
232232+233233+To me, the most critical part of becoming “so good they can’t ignore you” (h/t Cal Newport, Steve Martin) is to be “so prolific you don’t recognise yourself”. Once you cross that threshold you can actually look at your own work with a relatively objective, critical eye.
234234+235235+People are sometimes surprised to hear how agnostic and indifferent I am to specific methods, formats, tools. The only thing that really matters to me is a sense of flow and throughput through the entire pipeline.
236236+237237+Getting lost is a feature not a bug, the only real problem is getting jammed.
238238+239239+### ***Iron man suit*** {#iron-man-suit}
240240+241241+![][image8]
242242+243243+In Iron Man 2, when asked at a senate(?) hearing to describe the suit, which they are calling a weapons platform, Tony Stark defines it as a prosthetic. It seems funny, but it's also actually true.
244244+245245+I think of the suit (and accompanying AI) as an extension of Tony's mind. He built it to help him survive. It's a protective exoskeleton
246246+247247+A trope that moves me: "you got my back, now I got yours". In the above scene (from Iron Man 3), Tony's gift becomes his burden. I’ve sometimes felt this way about my own mind. I picked this scene as a header pic to remind myself to drag my own mind to a safe space \+ do the necessary repairs, so that we can flourish.
248248+249249+### ***Some random note-taking advice*** {#some-random-note-taking-advice}
250250+251251+Someone recently asked me for note-taking advice via DM, and I’m still thinking about what I told them because I think I accidentally stumbled upon a simple formulation that makes a lot of sense to me
252252+253253+Start with one notepad file, or one paper journal, whatever. Don’t think about topics, don’t think about how you’re going to sort it, what the sub-folders should be, etc. Just start dumping all your thoughts. Use line breaks, make it “chunky” (as opposed to long paragraphs)
254254+255255+Keep going until you \*begin to feel overwhelmed\*. Now pause. Your notepad file is now full of many discrete (as opposed to continuous) points, too many for you to remember or make sense of at a glance. Great\! Now it’s time to sort.
256256+257257+You now want to look for connections \*between\* nodes. Resist the urge to impose some sort of top-down grand scheme. Instead, look for related ideas, riffs, “things that go together”. “Siblings”. Here’s an eg of how I’m doing this retroactively on my blog
258258+259259+![][image9]
260260+261261+You see what’s happening? Dozens (100s if you nasty like me) of “atoms” start to coalesce into a smaller handful of “molecules”. You no longer need to consider and recall every single thing. You just need to remember the core molecules, and recognize “what goes with what”
262262+263263+The map of connections will actually reveal new insights to you. You’ll notice some molecules are larger or heavier or higher-value than others. I recommend framing each molecule as a sort of “expedition”, or “line of inquiry”. Each becomes a sort of investigation with related clues
264264+265265+I fell into a silly-ish trap a while ago where I went a little crazy and started creating too many blogpost drafts, too many new notes, too many folders \- and it became a hell to navigate. In retrospect it becomes clear: only “split” notes when it really makes sense to\!\!
266266+267267+[https://twitter.com/visakanv/status/1174067824412618752](https://twitter.com/visakanv/status/1174067824412618752)
268268+269269+### ***My style*** {#my-style}
270270+271271+quick sketch of my personal (ideal) style:
272272+273273+move fast, hit hard, wipe quick and move on to the next thing, but be micro-rigorous in making sure that each new thing quickly considers all past things, and is threaded accordingly.
274274+275275+my biggest weakness at the moment is actually "wipe quick" – I sit with half-done things for too long, but the real problem isn't that they're half-done, but that they're not properly threaded
276276+277277+properly-threaded half-dones are \*fine\*. lingering on a half-done for too long is not.
278278+279279+by "properly threaded" I mean contextualized against everything else that I'm doing, plotted on the (multi-thread, lol) map of my body of work. The act of contextualizing a thing – saying what it’s for, what it was trying to do, etc – makes it useful even if it’s incomplete.
280280+281281+Tk eh Idk if i want to include this section, would have to rewrite it. ([original thread](https://twitter.com/visakanv/status/1062324957902888961))
282282+283283+### ***Chaingun metaphor*** {#chaingun-metaphor}
284284+285285+*“Your brain is like a Ferrari, a race car. You have the power to win races and become a champion. However, you do have one problem. You have bicycle brakes. Your brakes just aren’t strong enough to control the powerful brain you’ve got. So, you can’t slow down or stop when you need to.” – Dr Edward Hallowell, on ADHD*
286286+287287+I sometimes think of the ADHD mind as a Gatling gun \- it takes a while to warm up, jams easily and is harder to aim, but once you appreciate its idiosyncrasies you can use it effectively
288288+![][image10]
289289+Shame I can’t put a gif in an ebook (actually, is it possible? – I could spend an entire day on this detour), because when you see what a complicated piece of machinery this is, and how “heavy duty” it is, it becomes clear how easily it could jam.
290290+291291+### ***It’s probably wise to seek a role that relies on your strengths*** {#it’s-probably-wise-to-seek-a-role-that-relies-on-your-strengths}
292292+293293+Sometimes when people ask me something like “how do you deal with your ADHD”, they’re really asking something like, “how do you live a normal life despite your ADHD”, but the true answer is “I don’t live a normal life”. I’ve chosen/designed a life around me, not the other way around.
294294+295295+This is a touchy/difficult topic to give advice about. Sometimes somebody comes to you asking how can I fix my relationship with my partner, when it’s clear that there’s no salvaging it and that they should exit the relationship as soon as possible. But communicating this can be challenging. You might get a “how dare you”, and so on.
296296+297297+Similarly... I don’t really know how to live a normal life. I suppose I sort of did for the 5.5 years that I held down a job, but even then I was kind of borderline weird. Y’know, like how you could kind of tell how Lady Gaga was even before she started going full Gaga.
298298+299299+### ***Go fast and get it all down*** {#go-fast-and-get-it-all-down}
300300+301301+IMO, and this is a deliberately provocative frame: cowardice disguises itself as confusion
302302+303303+children don't sit around think about what to do, they just do what they want
304304+305305+people already know what they want, in their hearts. thinking gets in the way and needs to be suspended
306306+307307+a sort of hack for thinkers to suspend thinking without suspending thinking is to go really, really fast and be really, really prolific. write or record (audio/video, whatever). then the truth will come out by accident and you can navigate by that
308308+309309+if you think "oh gosh I can't slow down, my mind goes a million miles a minute", put that mf
310310+ to work. write down every word, or record yourself speaking. can you write or talk for 8 hours straight? have you done it? do you have a record of it?
311311+312312+you may find that you start sputtering out at the 3 hour mark. what happens after that? you run out\! you run dry\! no thoughts head empty\! Peace\! or? you keep going, and you build a massive body of work, which will be full of unexpected value, if you went fast. either way you win.
313313+314314+(generalizing from dozens of DMs over the years)
315315+316316+the problem isn't that your mind goes fast. the problem is that you have your feet on both the gas and the brakes. take your foot off the brakes. your mind is racing because it wants to GO. why not let it?
317317+318318+people who feel stupid feel very smart about their self-assessment of "i'm stupid". you fucker\! you are smarter than you know, or CAN know. if you go faster than your mind's censorship department can keep up, you will spit wisdom and insight you didn't know you were capable of
319319+320320+Start with one notepad file, or one paper journal, whatever. Don’t think about topics, don’t think about how you’re going to sort it, what the sub-folders should be, etc. Just start dumping all your thoughts. Use line breaks, make it “chunky” (as opposed to long paragraph
321321+322322+###
323323+324324+### ***Useful actionable stuff goes here maybe, and/or a review of everything*** {#useful-actionable-stuff-goes-here-maybe,-and/or-a-review-of-everything}
325325+326326+Put down the gun – it’s very difficult to make progress while you’re being mean/cruel to yourself:
327327+328328+Take baby steps – it might seem childish, but that itself is a kind of dismissive cruelty. Greatness happens from small beginnings. If taking bigger steps hasn’t worked out so far, go smaller. You can be ferocious and intense about it
329329+330330+Leave notes for your future self –
331331+332332+Thread things
333333+334334+Get good at asking for help
335335+336336+Help people \+ ask for help in turn
337337+338338+### ***Being your own manager when you’re a feral adhd monke*** {#being-your-own-manager-when-you’re-a-feral-adhd-monke}
339339+340340+Just putting together a doc with shit i’ve learned. Which I suppose is the first point. Point One: put together docs of shit you’ve learned. You might think you don’t need it, but you’ll need it…
341341+342342+What else
343343+344344+Close your tabs. Use one-tab so you can put them all in one place
345345+346346+Currently experimenting with trying for more maker/manager distinction, like maker days and manager days, or pomodoros
347347+348348+The most important thing is your emotions, your psychology, how you’re feeling, what you care about, what you’re flinching from. Right now as I write this I kind of feel too tired to go through my playlists. Okay, so that’s not the sort of task I should be doing. Let’s… clear up the desktop.
349349+350350+Good reply game between notes. Try not to have orphan notes. Try not to have too many links either.
351351+352352+Do little things that you can celebrate. Momentum is worth it. Small dominos can knock over larger dominos.
353353+354354+### ***Note to self / stuff to expand on*** {#note-to-self-/-stuff-to-expand-on}
355355+356356+Want to respond to this tweet about motivation/executive control and medication [https://twitter.com/empathy2000/status/1466530296317288448](https://twitter.com/empathy2000/status/1466530296317288448)
357357+358358+4 hrs 40, 40 hrs 4
359359+[https://twitter.com/ZamiArts/status/1466438670764945408](https://twitter.com/ZamiArts/status/1466438670764945408)
360360+361361+![][image11]
362362+[https://twitter.com/MaximumADHD/status/1407550100679897092](https://twitter.com/MaximumADHD/status/1407550100679897092)
363363+364364+Gabor mate counterwill: [https://twitter.com/hormeze/status/1249750862454390794](https://twitter.com/hormeze/status/1249750862454390794)
365365+366366+Adhd drowsiness [https://twitter.com/roryreckons/status/1454697016626352128](https://twitter.com/roryreckons/status/1454697016626352128)
367367+368368+link from wayback machine as the original tweet has been deleted [https://web.archive.org/web/20211031062947/https://twitter.com/roryreckons/status/1454697016626352128](https://web.archive.org/web/20211031062947/https://twitter.com/roryreckons/status/1454697016626352128)
369369+370370+Thread about potential: [https://twitter.com/The\_Weed/status/1534592771792572418](https://twitter.com/The_Weed/status/1534592771792572418)
371371+372372+A recurring thing I’ve noted- & this has gotten worse because the illusion of my competence has gotten halo-effect’d – is that people, sometimes my closest friends, get surprised, even shocked, to find out my adhd “hard lines”. There are things I am almost incapable of doing….
373373+374374+###
375375+376376+### ***Conclusion and further reading and shit*** {#conclusion-and-further-reading-and-shit}
377377+378378+Tbc
379379+380380+Learn more about me: [visakanv.com](http://visakanv.com/)
381381+Follow me on twitter [@visakanv](http://twitter.com/visakanv) (DM me what you think\!)
382382+Subscribe to my youtube channel: [youtube.com/visakanv](http://youtube.com/visakanv)
383383+Check out my substack: [visakanv.substack.com](http://visakanv.substack.com)
384384+Buy my first book [FRIENDLY AMBITIOUS NERD](http://gum.co/fanbook)
385385+Buy my second [INTROSPECT](http://gum.co/introspect) (this is the better book, until I update FAN)
386386+387387+Thanks for reading\!
388388+389389+### ***Stray notes to myself*** {#stray-notes-to-myself}
390390+391391+Mykola adhd thread [https://twitter.com/mykola/status/1666274460935102464](https://twitter.com/mykola/status/1666274460935102464)
392392+393393+ADHD free for call? [https://twitter.com/caseyjohnston/status/1536441045977735169](https://twitter.com/caseyjohnston/status/1536441045977735169)
394394+395395+free for call pt 2 [https://twitter.com/pixelfish/status/1536572858175877121](https://twitter.com/pixelfish/status/1536572858175877121)
396396+397397+urge to abandon projects [https://twitter.com/visakanv/status/1553678532303536128](https://twitter.com/visakanv/status/1553678532303536128)
398398+399399+why do adhd ppl make overly large projects [https://twitter.com/danidonovan/status/1553775564477157376](https://twitter.com/danidonovan/status/1553775564477157376)
400400+401401+Make your life easier [https://twitter.com/imteddybless/status/1613169080940347399](https://twitter.com/imteddybless/status/1613169080940347399)
402402+403403+4 hours [https://twitter.com/dreamsofskies/status/1562528339331788803](https://twitter.com/dreamsofskies/status/1562528339331788803)
404404+405405+Shame [https://twitter.com/samdylanfinch/status/1563694311459680256](https://twitter.com/samdylanfinch/status/1563694311459680256)
406406+407407+ADHD very sensitive child
408408+409409+Gabor Mate: Look at the parental relationship, what stresses in the life,
410410+411411+ADHD as BS-protection?
412412+like, in one frame, and i'm not saying this applies to you, but for some people, their ADHD might be protecting them from living a BS life, and then the medication helps them to wear down that defense, so they can be motivated to do dumb BS. and for some people this is desirable\!
413413+414414+“something goes wrong and it destroys your ability to accomplish anything else” [https://twitter.com/CatieOsaurus/status/1579561996860915713](https://twitter.com/CatieOsaurus/status/1579561996860915713)
415415+416416+do it poorly [https://web.archive.org/web/20221019100128/https://twitter.com/andreagrimes/status/1580279730737008641](https://web.archive.org/web/20221019100128/https://twitter.com/andreagrimes/status/1580279730737008641)
417417+418418+Jack asks for focus tips [https://twitter.com/jappleby/status/1670793410284777473](https://twitter.com/jappleby/status/1670793410284777473)
419419+420420+James Stuber Threadapalooza Thread 2023: [https://twitter.com/uberstuber/status/1736489420466110843](https://twitter.com/uberstuber/status/1736489420466110843)
421421+422422+QC ADHD megathread: [https://twitter.com/QiaochuYuan/status/1757634307139707020](https://twitter.com/QiaochuYuan/status/1757634307139707020)
423423+424424+### ***Resummarizing / overviewing this whole doc*** {#resummarizing-/-overviewing-this-whole-doc}
425425+426426+1. Tiny Wins: i had become an untrustworthy person to myself, and the way I worked my way out of it was to practice making very small promises and fulfilling them immediately. Baby steps. Emotionally/psychologically the hard part here is accepting that, when you’re down bad, you can’t do big dramatic things to fix it. You have to accept being in the down state for some time. Being honest here will actually reduce the total amount of time in the downstate. There’s just less denial about it.
427427+2.