-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDEVLOG.txt
More file actions
736 lines (584 loc) · 37 KB
/
Copy pathDEVLOG.txt
File metadata and controls
736 lines (584 loc) · 37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
https://github.com/sooperD00/Connectome
C:\Users\Barac0da\source\repos\Connectome
# Deploy 1/??/2026
**Permanent (lives on its own):**
Railway is probably easiest:
- [ ] Push Connectome to GitHub (you have this)
- [ ] Railway dashboard → "New Project" → "Deploy from GitHub"
- [ ] Select your Connectome repo
- [ ] Railway will ask about build settings. Add:
- [ ] Build command: `pip install -r requirements.txt`
- [ ] Start command: `cd backend && uvicorn main:app --host 0.0.0.0 --port $PORT`
- [ ] Go to Variables tab → add `ANTHROPIC_API_KEY`
- [ ] Deploy
Railway gives you a URL like connectome-production.up.railway.app
Free tier gives you $5/month of usage. Plenty for demos.
⚠️ Cost warning: Your API key is in the deployed app. If your brother (or anyone) hammers it, you pay for the Claude calls. For showing family: fine. For sharing publicly: add rate limiting or auth first.
# start it up, furball
- [ ] source venv/Scripts/activate # activate env
- [ ] (venv) cd backend # the backend serves the frontend
- [ ] (venv) uvicorn main:app
- Flag = boolean, just present or not (--reload)
- Option = takes a value (--port 8000)
- [ ] http://localhost:8000 # chat
- [ ] http://localhost:8000/topology.html # graph
- [ ] http://localhost:8000/health.html # observability stub
- [ ] http://localhost:8000/docs.html # Swagger UI (Open API spec)
- [ ] http://localhost:8000/redoc.html # ReDoc UI (Open API spec)
- [ ] http://localhost:8000/???.html # somethin'
# Go break it
# Connectome Next Session Checklist 1/?/2026
## Smells
- [ ] topology.html d3 library sourcing dynamically: <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script>
1. Download D3:
Go to: https://d3js.org/d3.v7.min.js
Save it as frontend/d3.min.js (right-click → Save As, or curl/wget)
2. Update topology.html:
Find:
html<script src="https://d3js.org/d3.v7.min.js"></script>
Change to:
html<script src="d3.min.js"></script>
```
**3. Update main.py to serve it:**
It should already work since we serve static files from frontend, but verify you have a route or static file handler that covers `.js` files.
- [ ] Audit other external dependencies (fonts? icons?)
- [ ] Namespace generic CSS classes (e.g., `.container` → `.conn-container`) to avoid future framework collisions
- [ ] Audit for other generic class names: `.modal`, `.toast`, `.btn-*`
- [ ] topology.html Refresh button style and location - use shared button styles (pink or something) and put it *inside* the graph window if possible, like the legend
- [ ] shall I put on my "smell" list to DRY for the like... top of the HTML doc? to make a shared file for that so the title and nav menu always look the same?
- [ ] BE/main.py: @app.on_event("startup") doesn't like on_event (depricated?)
## Medium features (30-60 min)
- [ ] click on a node and generate new session (remember to ask about save first tho)
- [ ] Timer prompt: "Save your session?" after 5 min of activity
- [ ] Session topology preview on chat page (toggle-able)
- [ ] Pin topic UI (backend exists, needs button on topology nodes)
- [ ] Delete node UI (backend exists, needs button on topology nodes)
## Bigger features (1+ hour)
- [ ] Per-turn annotation boxes (attached to each message)
- [ ] Sticky notes skin/pinboard
- [ ] Prompt tuning for open-world vs closed-world seeds
- [ ] Profile management UI (group sessions, claim with account)
## Someday/maybe
- [ ] Sound design (Myst/Zork lever clicks)
- [ ] Hop distance slider for adjacent topics
- [ ] Comparison to education standards
- [ ] Image upload as seed ("look mom" feature)
---
# Session Checklist 1/22/2026 3pm
## Quick fixes (< 15 min each)
- [x] Change "Pending annotations" → "Annotations"
- [x] Allow send with only annotations (no text required)
- [x] Remove auto-refresh from topology.html (or make it optional)
- [x] remove auto-refresh
- [x] add a "refresh" button instead... currently link at top, not beautiful
- [x] CSS → separate `styles.css` file
- [x] styles.css with additional/specialized css still inside the html pages
- [x] update BE/main.py to serve styles.css (add a route)
- add near other static files
# Local Storage and restoring state 1/20/2026
1. localStorage layer (accumulate history + topology)
- [ ] new index.html
- [ ] extract css?
- [ ] // Merge edges (simple append, could dedupe later) - dedupe later!
- [ ] careful of naming bc waht about local db for montessori/homeschool
- [x] update get_session_by_key to return history from the BE
2. Save modal (visibility picker → POST /sessions → show key)
3. Load flow (URL param or input → GET /sessions/{key} → restore state)
4. Wire it all together
# Restructure for Complexity 1/17/2026
backend/
├── main.py # Just app setup + mount routes
├── routes/
│ ├── __init__.py
│ ├── chat.py # /chat endpoint
│ └── topology.py # /topology, /topology DELETE
├── db.py # get_db(), init_db(), update_topology_graph()
├── models.py # Pydantic: Annotation, Message, ChatRequest, ChatResponse
├── prompts.py # SYSTEM_PROMPT (it's 50+ lines, deserves its own file)
└── parsing.py # parse_topology_from_response, normalize_label
- [x] split up main.py - extract the pieces into propper architechture until it's a stub
- [x] models.py - The Data Contracts. DTO = data transfer objects (C# public record)
- FastAPI inherits (depends on) Pydantic
- yah we restructured this big time
- [x] (venv) pip install sqlmodel
- [x] update requirements.txt & environment.yml
- [x] db.py
- use `coolname` library (adjective-adjective-noun generation "educated-purple-lemur")
- ~4,500 words, curated to avoid awkward/offensive combinations
- Millions of combinations
- [x] (venv) pip install coolname
- [x] update requirements.txt & environment.yml
- test
- [x] (env) cd backend
- [x] (env) python -c "from db import init_db, generate_access_key; init_db(); print(generate_access_key())"
- [x] Should print something like "velvet-marsh-dawn"
- yah, I got "amazing-silent-grebe", took a hot min (5-10sec?)
- [x] and create a fresh connectome.db with all your tables
- [x] If you want to keep your old data, rename the existing connectome.db first — the new schema is different. I did, and i put it in .gitignore.
# 1/17/2026 Synthesis and Code Review
- [ ] FE: the map is not the reward
- [ ] Right and Left click for greed/pink highlight
- [ ] Keep the popup and add option text note to add, and an icon "button" for "find me pictures"
(checkbox but a bit fancier that looks like a button that stays in when pressed vs out)
- [ ] slider bar for structured <---> unstructured text output from claude
(paragraph blobs vs headers, bold, whitespace, lists, etc)
- [ ] Chat on the left, topology map + skins on the right
- [ ] session topology map as "base layer" in the main window
- [ ] combined topology map in the "topology" separate link
- Why no images/links in Connectome:
The app is calling the bare Claude API—just the /messages endpoint with a system prompt. No tools attached. The Claude you're talking to right now has web search, file reading, etc. Those are tools Anthropic added to this interface.
To add that to Connectome:
- [ ] Enable web_search tool in the API call
- [ ] Handle the tool-use response pattern
- [ ] Render images/links in the frontend
- [ ] that "save" feature that crawford asked for
- [ ] a "prune" feature that you can do in multiple places (ya know, I side track a lot)
- either topopology map, the boxes that persisted the [x topics], the sticky notes? need 2 delete icons on that so you don't have to ask every time "delete permanently" or just "remove sticky from the view for the pinboard"
- [ ] click a node and start a new session with that as the seed
- [ ] save the whole session as compressed text? into the database in 1 col (ask for best format)
- [ ] profile0001 so I can split the session groups
- [ ] doc reading skin \[Read\]
- [ ] pinboard skin - notes
pin the "highlight text" as a sticky note title with "comments/questions/details" as smaller text on the pin board
- [ ] ttrpg skin, with yellow/blue for historical and ahistorical when playing/creating historical fiction characters and worlds. Maybe the user highlights something and asks claude (popup button: "real or fake"? and it comes back with the color
- [ ] wordpress skin for Write
- [ ] pink/green need to persist in the chat text
- [ ] annotation phrases box needs to persist in between each dialogue turn
- [ ] Close the skin → map animates the new nodes appearing; pinboard is default, unless user chooses a new one
- [ ] Make skin: Hands-on, terse, step-by-step, real-world (Cheese & Yogurt)
- [ ] be able to take a "look mom" (look what I made, look waht I found) type pic upload as the seed, like a toy semaphore
- [ ] comparison to different kinds of education standards, not to be prescriptive, just because sometimes standards and benchmarks are reasonable tools for identifying gaps and creating common pools of knowledge/reference framing in society
- [ ] Sound design: Myst/Zork aesthetic
- Freesound.org for CC mechanical sounds
- ZapSplat for puzzle/lever sounds
- Clunk on save, soft whir on topology animation
- NO achievement sounds, NO dings for "progress"
- [ ] BE: I haven't used most of the packages in the header
- [ ] I've never used a class in python, only C#
- [ ] I did not know you could... strongly type something in python?
`def normalize_label(label: str)`
Yes, label: str and -> str are Python type hints
Optional at runtime (Python ignores them)
but Pydantic enforces them for the BaseModel classes
C# DTOs (Data Transfer Object) / record types
= Data containers with automatic validation
Python's way of getting closer to C#'s type safety without going full TypeScript
- [ ] I see that you make a SQLite database with a couple tables that ostensibly is best practices for defining topology nodes edges and relationships in a graph
- [ ] You are using the conn.stuff that I do know about
- [ ] Looks like schema definition that I can grok
- [ ] Then it looks like you are "loading" anthropic and defining the...pieces? Containers? For the messages, the json for the annotations, and the instructions in the prompt are clear
- [ ] I think we might want to add something later when we add the "read" stuff for when the user *wants* you to explore outside articles, and they can read them too in the "skin" area, but that's for later (sometimes you say I don't know when you are a powerful lookup tool)
- [ ] I see you processing the user inputs into nodes etc
- [ ] I see you processing the pieces of the user and the LLM response that go into the database and putting it in
- [ ] I'm not sure i would have thought about this step using `for` loops
You're right to side-eye them
🎯 In production with volume, you'd batch insert
- [ ] I'm also not sure what defensive coding is there, if any
- [ ] if the LLM response is not actually locked in to send the data you're asking for based on your "MUST" statement in the prompt. This is maybe sloppy API handoff?
🎯 This is a real weakness. The parse_topology_from_response has a try/except, so bad JSON won't crash. But if Claude just... doesn't send topology? We silently proceed with None. No retry, no logging, no alert. For production:
- [ ] Log when topology is missing
- [ ] Maybe retry once with a nudge
- [ ] Track success rate over time
- [ ] It looks like you built in an observability stub at / health. That is really nice
- [ ] At /topology you have done SELECT * and I'm not sure how this code will handle a lot of data, and if the packages implement navigation for us... because obs you have to limit the view to a human or to a finite number of pixels etc etc when it's large
🎯 Another real catch. This will choke on thousands of nodes. Fixes for later:
- [ ] Pagination / viewport-based loading
- [ ] Cluster nodes at zoom-out levels
- [ ] Time-window filtering ("just show last 30 days")
- [ ] Cap node count, prioritize by recency/weight
# 1/13/2026 Init
Open
- [x] File → Open Folder → navigate to connectome/
- [x] ctrl+j # check bash
Env & Requirements # no version pins to start - let pip do it
- [x] python -m venv venv # create env
- [x] source venv/Scripts/activate # activate env
- [x] (venv) pip install -r requirements.txt # Conda's superpower is managing non-Python dependencies (C libraries, CUDA, etc.)—you don't need that here.
- [x] (venv) pip list
- [-] (venv) pip freeze > requirements.txt # requirements.txt (pip) AND environment.yml (conda-can do extra-python dependencies) is common pattern, but not needed here. Actually I will do this manually but check out pip-tools below
- [x] (venv) pip freeze | grep -iE "^(fastapi|uvicorn|anthropic|python-dotenv|pydantic)=="
Anthropic API key
- [x] (venv) cp .env.example .env
- [x] (venv) cat .env
- [x] (venv) console.anthropic.com > pay $5 > get API key
- [x] (venv) grep -E "\.env|DEVLOG" .gitignore ## MAKE SURE THESE ARE THERE!!!
- [-] (venv) echo "ANTHROPIC_API_KEY=sk-ant-api03-xxxYOURKEYxxx" > .env # you did the paste in another file bc you are paranoid
- [x] (venv) cat .env # verify it took
Run the backend
- [x] (venv) cd backend
- [x] (venv) uvicorn main:app --reload
Verify in Browser
- [x] http://localhost:8000 # chat
- [x] http://localhost:8000/topology.html # graph
holy fuck it works
# The pin board skin: 🎯
This is better than I had. The flow:
Chat (left) → Pin board (right, during) → Topology (right, after)
LayerWhenWhatPin boardDuring conversationSticky notes accumulate: highlight = title, your comment = bodyTopologyAfter / reflectionNodes + edges, the map
>Pin board is note-taking. Topology is pattern-seeing. Different cognitive modes. Both real.
# Common Core 1/17/2026
This is the legibility move that isn't Moloch.
The diff works both ways:
Your topology vs. StandardRevealsWhat you covered"You've hit 80% of 8th grade physical science"What you're missing"Gap: you haven't touched waves/sound"What you learned that isn't in the standard"You know fermentation, cryptography, semaphore history—none of this is measured"
Standards as importable overlays:
anti-coercive legibility.
Common Core
State standards
AP curricula
Trade certifications
Self-defined goals ("I want to understand X by spring")
# ASCII 1/17/2026
┌─────────────────────┬─────────────────────┐
│ │ │
│ CLAUDE │ TOPOLOGY │ ← home state
│ (chat) │ (map) │
│ │ │
│ │ ○──○ │
│ │ ╲ ╱ │
│ │ ○ │
└─────────────────────┴─────────────────────┘
↓ open Read skin
┌─────────────────────┬─────────────────────┐
│ │ ┌─────────────────┐ │
│ CLAUDE │ │ Mary Oliver │ │ ← skin active
│ (chat) │ │ ═══════════ │ │
│ │ │ You do not │ │
│ "what does she │ │ have to be │ │
│ mean by soft │ │ good. You do │ │
│ animal?" │ │ not have to... │ │
└─────────────────────┴─┴─────────────────┴─┘
↓ close skin
┌─────────────────────┬─────────────────────┐
│ │ │
│ CLAUDE │ TOPOLOGY │ ← map updated!
│ (chat) │ (map) │
│ │ │
│ │ ○──○ ◌ wild │
│ │ ╲ ╱ ◌ geese │
│ │ ○──○ Mary Oliver│
│ │ ◌ attention │
└─────────────────────┴─────────────────────┘
# Uvicorn
the server that runs your FastAPI app.
FastAPI is just Python code defining routes. It doesn't listen on a port by itself.
Uvicorn:
- Opens port 8000 (or whatever you specify)
- Listens for HTTP requests
- Passes them to FastAPI
- Returns responses
It's an ASGI server—the async successor to WSGI (which you'd see with Flask/Gunicorn).
Browser request
↓
Uvicorn (server, handles HTTP)
↓
FastAPI (your code, handles routing/logic)
↓
Response back up
The --reload flag: watches your files, auto-restarts when you save. Dev mode only—don't use in production.
Production alternative: You'd put Uvicorn behind nginx or run with Gunicorn managing multiple Uvicorn workers. But for local dev, uvicorn main:app --reload is the move.
---
# WSGI / ASGI
WSGI = Web Server Gateway Interface
ASGI = Asynchronous Server Gateway Interface
They're just standards for how a Python web app talks to a server:
WSGI (old, synchronous):
Request comes in → blocks until response ready → next request
ASGI (modern, async):
Request comes in → can handle other requests while waiting → concurrent
FastAPI uses ASGI (async). Django historically used WSGI but now supports ASGI too. Flask is WSGI.
---
# ORM = Object-Relational Mapping
Instead of writing raw SQL:
sqlSELECT * FROM users WHERE id = 5;
You write Python:
pythonuser = User.objects.get(id=5) # Django ORM
The ORM translates your Python objects ↔ database tables. Django's ORM is built in. For FastAPI/Flask, you'd add SQLAlchemy.
In Connectome, you're writing raw SQL to SQLite:
pythonconn.execute("SELECT * FROM nodes WHERE id = ?", (label,))
With an ORM you'd have a Node class and do Node.get(id=label). More abstraction, less SQL, but same result.
One-liner versions:
WSGI/ASGI: How Python talks to web servers (sync vs async)
ORM: Python objects instead of raw SQL
Get a user by ID:
python# Raw SQL (what you have in Connectome)
conn.execute("SELECT * FROM users WHERE id = ?", (5,)).fetchone()
# SQLAlchemy Core (query builder)
select(users).where(users.c.id == 5)
# SQLAlchemy ORM (what most people mean by "SQLAlchemy")
session.query(User).filter(User.id == 5).first()
# SQLAlchemy 2.0 style (newer)
session.scalars(select(User).where(User.id == 5)).first()
# Django ORM
User.objects.get(id=5)
csharp// EF Core (your familiar ground)
context.Users.Where(u => u.Id == 5).FirstOrDefault();
// or
context.Users.Find(5);
Insert a record:
python# Raw SQL
conn.execute("INSERT INTO users (name, email) VALUES (?, ?)", ("Nicole", "n@x.com"))
# SQLAlchemy ORM
user = User(name="Nicole", email="n@x.com")
session.add(user)
session.commit()
# Django ORM
User.objects.create(name="Nicole", email="n@x.com")
# or
user = User(name="Nicole", email="n@x.com")
user.save()
csharp// EF Core
context.Users.Add(new User { Name = "Nicole", Email = "n@x.com" });
context.SaveChanges();
The mental mapping:
EF CoreDjangoSQLAlchemyDbContextImplicit (model manager)SessionDbSet<User>User.objectssession.query(User).Where().filter().filter() / .where().FirstOrDefault().first() / .get().first().Add() + .SaveChanges().save() or .create().add() + .commit()Migrations via CLIpython manage.py migrateAlembic (separate tool)LINQDjango QuerySetSQLAlchemy Query
Key differences:
DjangoSQLAlchemyEF CoreBundled?Yes, with DjangoNo, standaloneYes, with .NETMigrationsBuilt inAlembic (add-on)Built inAsyncNew, partialFull supportFull supportQuery styleChained methodsChained or SQL-likeLINQLearning curve from EFLowLow—
lolololololo
TypeScript exists because backend devs said "I'm not writing a banking system in a language that lets me do '5' - 3 = 2 and '5' + 3 = '53'." Types make it grown-up enough for serious work.
# pip freeze and pip-tools
Good question! Actually pip list and pip freeze do NOT include standard library—those are built into Python, not installed.
What you're seeing is transitive dependencies. You asked for fastapi, but fastapi needs starlette, which needs anyio, which needs... and so on.
Your options:
1. Full freeze (reproducible but noisy):
bashpip freeze > requirements.txt
```
Shows everything. Guarantees exact reproduction. Standard for production.
**2. Manual minimal file (human-readable):**
Just list your direct deps in `requirements.txt`:
```
fastapi
uvicorn[standard]
anthropic
python-dotenv
pydantic
Pip will grab their dependencies automatically. Less precise but cleaner.
3. Best of both worlds—pip-tools:
bashpip install pip-tools
```
Create `requirements.in` (your direct deps):
```
fastapi
uvicorn[standard]
anthropic
python-dotenv
pydantic
Then:
bashpip-compile requirements.in
Generates requirements.txt with full pinned versions + comments showing why each package is there.
For Connectome: manual minimal is fine. For Tomo-level production: pip-tools is the grown-up answer.
# Product Name
Level 1: Punchy Title + Subtitle
Connectome — A curiosity navigation system
Level 2: GitHub description (350 chars)
This is where I need to balance soul + tech credibility.
Human-in-the-loop LLM orchestration with structured signal extraction and session persistence. Annotate confusion and curiosity—feedback shapes responses, topology accumulates over time. API-first design, no gamification. FastAPI + Claude + SQLite + D3. You wander. It maps.
Level 3: README opening paragraph
This can be longer, more detailed, show the architecture thinking. This is where the full vision can come through.
Connectome treats curiosity and confusion as first-class signals. Instead of static Q&A, users annotate what felt unclear or interesting, rank intensity, and watch those signals shape the next response. Structured extraction pulls feedback into a persistent topic graph that accumulates across sessions—a navigable map of your own learning trajectory.
Built as an API-first, human-in-the-loop LLM orchestration layer. Core patterns: feedback-driven personalization, session persistence, EWA-based relevance decay, and force-directed visualization. Designed to explore how memory, topology, and feedback can support open-ended learning without badges, streaks, or dark patterns.
Stack: FastAPI, Claude API, SQLite, D3.
You wander. It maps. Side effects may include genuine curiosity.
product thinking + backend rigor + LLM literacy without sounding like a research paper or a hype deck
progression: vision → transferable engineering → user features → implementation details
# FastAPI inherits (depends on) Pydantic
- Pydantic (2017) BaseModel gives you three things for free:
1. Validation — If the frontend sends {"level": "banana"}, Pydantic rejects it before your code ever sees it. You don't write if not isinstance(level, int) anywhere.
2. Serialization — .dict() and .json() methods, automatic conversion to/from JSON in FastAPI routes.
3. Documentation — FastAPI reads these classes and auto-generates OpenAPI/Swagger docs. Your FE person can hit /docs and see exactly what shape the request needs to be.
# Swagger & OpenAPI
Swagger (2011) isn't a package. OpenAPI isn't a package. They're a specification.
a way to describe REST APIs in a machine-readable format. The idea: write a JSON/YAML file that says "this endpoint takes these parameters and returns this shape," and tools can auto-generate documentation, client libraries, even mock servers.
in C#," you're probably using Swashbuckle or NSwag — libraries that:
Read your C# controllers and models
Generate an OpenAPI spec (a JSON file describing your API)
Serve Swagger UI (the pretty interactive docs page)
Your Pydantic models (ChatRequest, ChatResponse)
↓
FastAPI reads the type hints
↓
Generates OpenAPI spec (JSON at /openapi.json)
↓
Serves Swagger UI (at /docs)
Go try it right now: With your backend running, hit http://localhost:8000/docs. You'll see interactive documentation for /chat, /topology, /health — auto-generated from your code. You can even test the endpoints from the browser.
There's also http://localhost:8000/redoc — same OpenAPI spec, different UI skin (ReDoc instead of Swagger UI).
fukin no way
# Myst Soundscape
The actual Myst sounds: Copyrighted by Cyan, Inc. Not available.
But the aesthetic you want is totally achievable:
SourceLicenseCostVibeFreesound.orgCreative Commons (varies)FreeSteampunk, mechanical, crank sounds, "mechanism stuck"ZapSplatRoyalty-freeFree"Ancient game sound, steampunk gear mechanism or lever" — literally what you wantSonniss "Steampunk Gadgets"Royalty-free~$30"Buttons, Clicks & Switches", "Gear Wheels & Rotation", "locking mechanism of a puzzle box"
The connection you noticed:
Myst and Zork are both exploration as reward. No XP bar. No loot drops. Just: "I wonder what this lever does." That's exactly what Connectome is — the topology map is your reward for being curious, not a gamified progress meter.
The sound design reinforces that. A satisfying clunk when you save a session. A soft mechanical whir when the topology animates. It's feedback that says "something happened" without saying "you leveled up!"
# Django = Django Reinhardt
Belgian-Romani jazz guitarist from the 1930s-40s. Considered one of the greatest ever. The framework creators were journalists in Kansas who loved jazz. Same energy as naming things after jazz clubs (Flask, etc.) or Monty Python. It's a vibe, not an acronym.
Yes, Django's "View" is the Controller. Everyone finds this annoying.
In your Connectome terms:
models.py → defines Node, Edge, Visit (your SQLite tables)
views.py → your route handlers (chat, topology, health)
templates/ → your index.html, topology.html
urls.py → the @app.get("/path") decorators, extracted to a file
MVC → Django mapping:
Django says "MTV" (Model-Template-View) but it's the same idea, just renamed confusingly:
MVCDjangoJobModelModelData, ORM, business logicViewTemplateWhat the user sees (HTML)ControllerViewHandles request, calls model, picks template
The honest truth: Django's async is newer (2020+) and still a bit awkward. FastAPI was async-first from day one. If Tomo is doing heavy async work, they might have FastAPI for some services, Django for the main app. Hybrid stacks are common.
# uvicorn
uvicorn is an ASGI server — it's the thing that actually listens for HTTP requests and passes them to your Python code. FastAPI is a framework that defines what to do with requests; uvicorn is the engine that receives them.
main:app means:
main = the file main.py (no extension)
app = the variable inside that file (app = FastAPI(...))
So uvicorn main:app = "run the app object from main.py"
How it knows the folder: It doesn't, really. You cd backend first, then run uvicorn. It looks for main.py in your current directory. If you ran it from root, it would fail with "module not found."
How it serves frontend: It doesn't automatically. Look at the bottom of your main.py:
python@app.get("/")
async def root():
return FileResponse("../frontend/index.html")
@app.get("/topology.html")
async def topology_page():
return FileResponse("../frontend/topology.html")
Those routes manually serve the HTML files. FastAPI is acting as both API server AND static file server. In production you'd often have nginx serve static files instead, but for dev this works fine.
Port 8000: That's uvicorn's default. You can change it with uvicorn main:app --port 3000 or whatever.
Most people say "you-vee-corn" — rhymes with unicorn.
It's a portmanteau: uvloop (a fast async library) + unicorn (from Gunicorn, "Green Unicorn", an older Python server).
So it's kind of a "fast unicorn" pun.
libuv — a C library for async I/O that was built for Node.js.
What "uv" stands for in libuv? Unclear. Some say it was just arbitrary letters, some claim it's a joke acronym that never got a straight answer from the creators.
So the lineage is:
libuv (C library, powers Node.js)
↓
uvloop (Python wrapper around libuv)
↓
uvicorn (ASGI server using uvloop)
Sometimes names just... happen.
# CSS :root
:root variables: Used everywhere, defined once
Not inheritance — in CSS the : means something different than C#. It introduces a "pseudo-class" (a special selector). :root just means "the top-level element" (basically <html>). (:hover is another one)
We put variables there because CSS variables cascade downward — define once at the root, available everywhere below.
On the palette:
Yes, exactly right. --pink: #ff6b9d defines it, var(--pink) uses it. Change the definition once, updates everywhere. Same idea as constants in code.
On combining body definitions:
Yes. Both files had:
cssbody {
font-family: ...
background: var(--bg);
color: var(--text);
margin: 0;
/* etc */
}
Slightly different in each. I unified them in styles.css. If one page needs something different, it can override in its own <style> block — CSS "cascades," meaning later definitions win.
# CSS Reset (*)
* = select every element on the page (universal selector)
box-sizing: border-box = when you set width/height, include padding and border in that measurement
Without it (the default content-box):
width: 100px + padding: 10px + border: 2px = 124px total
With border-box:
width: 100px (padding and border fit inside) = 100px total
It's a sanity reset. Almost every modern project uses it because the default math is confusing. When you say "this box is 100px wide," you want it to actually be 100px, not 124px.
# CSS Body
:root {
--bg: #1a1a1a; /* defining a variable */
}
body {
background: var(--bg); /* using that variable */
margin: 0; /* also setting a regular property */
}
# CSS selector syntax
This is a fundamental CSS concept:
.classname selects elements by their class attribute (class="classname")
tagname selects elements by their HTML tag name
In our HTML:
<div class="container"> - we use .container because it's a class
<header> - we use header because it's an HTML tag, not a class
This is a great question to understand CSS selectors.
. means "select by class attribute"
No . means "select by HTML tag name"
In your HTML:
html<div class="container"> <!-- selected by .container -->
<header> <!-- selected by header (it's a tag) -->
If you had <div class="header">, you'd use .header.
Since <header> is an actual HTML element (like <body>, <nav>, <div>), you select it directly without the dot.
there's no HTML element called "container" and no built-in CSS class called ".container". It's just a very common convention that many frameworks use (Bootstrap, etc.).
However, the user raises a valid concern - if they ever add a CSS framework like Bootstrap, there could be a collision since Bootstrap also uses ".container" as a class name.
For a small project like this, it's fine. But it's a good instinct to question generic names.
It's just a very common convention. Bootstrap uses .container, Tailwind uses it, everyone uses it. It's like naming a variable data or result — generic, but not reserved.
The actual risk: If you later add a CSS framework (Bootstrap, etc.), their .container could clash with yours. Solutions:
Namespace it: .connectome-container
Or just don't add Bootstrap
For a solo project with no frameworks, .container is fine. Your instinct to question it is right though — generic names in shared codebases cause collisions
# HTML <a ... > tag
a = anchor tag
href (<a href=) is a property
<a> = anchor
It's called that because originally it "anchored" one part of a document to another — like internal page links (#section-name).
Now it's just "the link tag":
html<a href="https://google.com">external link</a>
<a href="/about">internal link</a>
<a href="#section">jump to section on same page</a>
<a href="mailto:me@email.com">open email client</a>
<a href="tel:555-1234">call on mobile</a>
<a href="file.pdf" download>download a file</a>
The href = "hypertext reference" (where it goes).
It does a lot for one letter.
what would you use "a" for if it *wasn't* for an href?
Actually, you can use <a> without href:
As a placeholder for a link that will be added dynamically via JavaScript
As a named anchor target (old style, now we use id="" on any element)
Sometimes people misuse it as a clickable thing that runs JavaScript (onclick) but this is bad practice - should use <button> instead
The honest answer is: you generally shouldn't. If it's not a link, use <button> or another element. An <a> without href is semantically weird.
Honestly? You probably shouldn't.
Old HTML used <a name="section"> as a target to jump to. Now we just use id="" on any element.
If it's clickable but not a link, use <button>. Using <a onclick="..."> without href is a code smell — it confuses screen readers and breaks keyboard navigation.
Rule of thumb:
Goes somewhere → <a href="...">
Does something → <button>
why did they stick with <a href> instead of chanign to <href> in newer standards?
Backwards compatibility.
Billions of pages use <a href>. If they changed it, the old web breaks. HTML/CSS/JS are obsessively backwards compatible — code from 1995 still runs in modern browsers.
Also, <a> takes other attributes:
html<a href="/file.pdf" download target="_blank" rel="noopener">
So <a> is the element, href is just one of its properties. Same pattern as <img src="..."> — the tag and its attributes are separate concepts.
ahh ok here's the answer. href is a property. you would "use a" for fthe other properties ;) not what I was originally thinking but... I kinda am remembering this
The user is having an "aha" moment - realizing that <a> is the element/container, and href, download, target, rel are all just properties of that element. So you "use <a>" because it's the anchor element that can have multiple properties, not just href.
They're learning and connecting the dots. I should affirm this realization without over-explaining.Exactly. <a> is the container, and you can hang multiple properties on it:
html<a
href="/file.pdf" <!-- where -->
download <!-- save it, don't navigate -->
target="_blank" <!-- new tab -->
rel="noopener" <!-- security -->
>
Download PDF
</a>
Same element, different behaviors based on which properties you use. The tag name is the noun, properties are the adjectives.
# "the nav" / <nav>
So yes, <nav> contains the "Chat" and "Topology" links. It's not the whole header stripe - that's <header>. The nav is just the links portion inside the header.
<header>
<h1>Connectome</h1>
<nav> <----------just this section *inside* the header, hence `header nav{}`
<a href="/">Chat</a> <--------you just made some links but it could be anything
<a href="/topology.html">Topology</a>
</nav>
</header>
<nav> is just any navigation section - it could be:
A horizontal link bar (what we have)
A hamburger menu
A sidebar menu
A footer with links
Breadcrumbs
The hamburger icon (☰) is just one way to show navigation on mobile. The <nav> tag doesn't care about the visual style.
Same thing, different presentation. <nav> is just "this is navigation" — doesn't specify how it looks.
Desktop: often a horizontal bar of links (what you have)
Mobile: often collapses into a hamburger ☰ that expands
Both would use <nav> in the HTML. CSS decides whether it's a bar, a dropdown, a sidebar, whatever.
what does a <nav> tag in html get you beyond just some other <div>?
Semantics — it tells browsers and tools what this thing is, not just how it looks.
BenefitWhy it mattersScreen readers"Navigation region" announced to blind users, they can skip to it or past itSEOSearch engines understand your page structureBrowser featuresReader mode, "find navigation" shortcutsCode readabilityYou see <nav> and know what it is vs. <div class="nav-thing">
Functionally identical to <div>. Semantically meaningful.
Same idea: <header>, <footer>, <main>, <article>, <section> — all just divs with meaning.
# CSS selector syntax - descendant selector
Yes. Space = "inside of" (descendant).
cssheader nav { } /* nav inside header */
header a { } /* a inside header */
header nav a { } /* a inside nav inside header */
Other patterns you'll see:
cssheader, nav { } /* comma = both header AND nav (separate) */
header > nav { } /* > = direct child only, not nested deeper */
header.big { } /* no space = header WITH class "big" */