-
Notifications
You must be signed in to change notification settings - Fork 0
Design Tradeoffs
Mehmet Nuraydın edited this page Jul 19, 2026
·
1 revision
Every design choice was deliberate. Here is what I chose, what I chose it over, and why.
| I chose | Over | Why |
|---|---|---|
| Local files | Jira, Linear, GitHub APIs via MCP |
Read on a local file never fails. Filesystem speed beats network speed. And I can open the file myself. |
| Smart pre-loading | On-demand search | A structured snapshot once is cheaper than repeated search loops. Soul, user, memory load in full. Everything else loads as summaries with paths. |
| Opinionated structure | Flexible layout | The snapshot knows where everything lives because it is always in the same place. Flexibility means discovery, discovery means search, search means tokens. |
| JSON changelogs | Markdown or git log | JSON is the agent's native format. Discrete fields, instant parsing, one CLI call to append. |
| Keyword search | Semantic/vector search | Zero dependencies, instant, predictable. Dozens of context files don't need embeddings. |
| CLI for structure | Agent editing JSON/frontmatter directly | One CLI call beats a Read + reason-about-format + Edit + verify cycle. |
| Hooks over prompts | Instructions in CLAUDE.md | When sleep tracking lived in prompt instructions, the agent forgot to consolidate most of the time. Hooks fire consistently regardless of context pressure. |
| Human-readable files | Agent-only memory stores | If I cannot read what the agent knows, I cannot correct it. Markdown and JSON are readable by both. That is the whole point. |
| Claude Code first | All agents at once | Starting where the hook and tool ecosystem is richest. More agents coming as I expand. |
| Native HTML drag-and-drop | @dnd-kit or react-dnd | Zero dependency cost. Upgrade later if UX is insufficient. |
| No CSS framework | Tailwind, CSS Modules | Full control over design system. Custom tokens, brand gradient, light/dark mode. Minimal bundle. |
| Native Node HTTP server | Express, Fastify | Zero new runtime deps for the dashboard server. Routes wrap existing src/lib/ utilities. |
| SVG bezier curves for ER diagrams | D3, React Flow | Graph libraries are overkill for a static schema view. SVG paths with cubic bezier and arrowheads over CSS grid. |
| Field-level change tracking | "Entity changed" records | The agent needs to know what changed, not just that something changed. Net-change detection keeps the list clean. |
| Bookmarks over equal processing | Process all sessions the same | Without salience tagging, the sleep agent has no way to distinguish a critical constraint from a routine fix. Bookmarks give it a priority signal. |
| Structural distillation | AI-based summarization | Pure Node.js JSONL filtering is instant, deterministic, and free. Tool name pattern matching is sufficient to separate signal from noise. |
| Dual-signal debt scoring | Change count only | Bash-heavy sessions or deep research with no file writes were invisible. max(changeScore, toolScore) ensures all meaningful work registers. |
| Warm knowledge tier | Binary pinned/indexed | A file you read yesterday should not be as cold as one from 6 months ago. First-paragraph previews give the agent enough context to decide without loading everything. |
| Triggers over memory scanning | Agent re-reads memory.md | Prospective memory ("do X when Y") is a stored intention, not a recall task. Triggers surface automatically when the right context appears. |
| Session rhythm advisory | Crisis-driven consolidation only | Regular small consolidations (every 5 sessions) keep context fresh. Waiting for debt 10+ means a massive catch-up job with reduced quality. |
| PreToolUse deny for Explorer | SubagentStart injection only | Explorer's built-in system prompt overrides additionalContext. The only way to enforce context-first behavior is to replace the default Explorer entirely via a deny hook. Plan is additive and works fine with injection. |
| UserPromptSubmit for debt reminders | SessionStart-only reminders | SessionStart fires once per session. Agents dismiss it under context pressure. UserPromptSubmit fires on every user turn, making the reminder persistent and undismissable. |
| PostToolUse auto-format + tsc | Manual formatting and type-checking | Errors caught in real-time after each edit are cheaper to fix than accumulated errors found at test time. Single hook handles both (sequential execution, one process). |
| execFileSync over execSync | Shell string interpolation | File paths with special characters ($, spaces, backticks) are safe with array arguments. Never regress to execSync with string interpolation in hooks. |
| PreCompact audit trail | Ignoring context compaction | When the agent loses context mid-session, understanding when and why helps debug behavior gaps. 20-entry cap prevents unbounded growth. |
| Pattern extraction in sleep | Manual knowledge curation only | Recurring patterns across sessions (user preferences, workflow sequences, errors) are automatically surfaced by the sleep agent and written to memory or knowledge files. |
| Unified versions/releases | Separate VERSIONS.json | A version is just a release that hasn't shipped yet. One file, one lifecycle (planning -> released). Less complexity, less code, and the sleep agent can check version readiness during consolidation. |
| Eisenhower matrix excludes completed | Show all tasks | The matrix is for deciding what to do next. Completed tasks are noise in that context. Kanban still shows them for a complete project view. |
| Skill packs as flat file copies | npm sub-packages, dynamic loading | Skills are markdown files. Copying them to .claude/skills/ is the simplest install mechanism. No package resolution, no runtime dependency. The agent reads them as local files. |
| Interactive checkbox for pack selection | CLI flags only | Developers want to browse what's available before committing. The terminal UI shows descriptions, sub-skill counts, installed status, and cross-pack warnings in one view. Direct flags (--packs engineering) still work for scripting. |
| Council rounds over single-pass debate | One-shot multi-agent debate | Single-pass collapses into averaging (if all personas see each other's drafts) or anchoring (whoever spoke first sets the frame). Rounds with blind R1 then cross-context R2+ produce genuinely independent positions that then sharpen against each other. |
| Dynamic final-report sections | Hardcoded Why/Minority/Risks template | Real synthesizer output varies by decision: a hiring review needs "What was missing", a migration needs "Rollback strategy", a brand critique needs "Revision priorities". Parsing whatever ## sections the synthesizer emits matches the shape of the decision. |
| Inline cell expand in matrix | Side drawer or modal | Drawer pushes the matrix out of view. Modal breaks scan-ability. Expanding the cell in place lets you see full round content without losing the grid context. |
| Problem as hero on Overview tab | Topic in the header | For a completed debate, the user is there to read the verdict against the original question. Making the Problem the visual anchor ensures "what did we decide about X" is always answerable in one scroll. |
| Obsidian integration by vault config, not plugin | An Obsidian plugin | A plugin is installable-software the user has to manage. A .obsidian/ config is a directory of JSON files that travels with the repo and works for anyone who opens the folder. |
| Full project build after dashboard changes | Per-package build | The CLI serves from dist/dashboard/ (sibling of dist/index.js). The tsup onSuccess hook copies dashboard/dist/ → dist/dashboard/. Only the root npm run build triggers both. Building just the dashboard silently leaves the served version stale — learned the hard way. |
| BM25 over the curated corpus | mem0 / Python + Ollama vector store | Three independent reviewers rejected the vector plan: the LLM extraction step solves a problem dreamcontext already solved (content is already curated atomic facts). BM25 is deterministic, instant, version-controllable, zero new deps. ~80% of the value at 1% of the complexity. |
| In-memory index rebuild per query | Persistent index file | At ≤500 docs the rebuild is under 100ms. A persistent index would add gitignore complications and cache-invalidation bugs for negligible speedup. |
| Memory hook ON by default | Off-by-default opt-in | Initially shipped opt-in per the security reviewer's "off by default" recommendation. Flipped to default-on the same day after dogfood showed the score ≥2.0 filter + 8-char minimum keep noise low and utility high. Opt out with DREAMCONTEXT_MEMORY_HOOK=0. |
| Loopback bind + CSRF/CORS guard |
0.0.0.0 bind, wildcard CORS |
The dashboard edits local files over unauthenticated routes — safe only if it is reachable by you alone. Binding to 127.0.0.1, an Origin/Host check on writes, and loopback-only CORS close LAN exposure and browser-CSRF-to-localhost. Hardened before first publish. |
| Version check off the hot path | Check at session start | Session start must stay instant. The snapshot reads a cached result only; the one npm call runs ≤1×/24h from UserPromptSubmit, fail-silent, opt-out via DREAMCONTEXT_VERSION_CHECK=0. A blocking check at session start would defeat the product's core promise. |
| CLI/project update split | One "update" command | The global CLI and the per-project installed files are different artifacts. upgrade moves the binary; update propagates it into a project. Conflating them is the usual "I updated but nothing changed" trap. The curl … | sh one-liner does both. |
| Full skill catalog in the context gate | Pre-picked top-3 skills | An earlier gate listed a top-3 skill subset; removed after four iterations. Narrowing the agent's view of its own toolkit was worse than telling it to scan the whole (already-injected) catalog and decide. The relevance check only gates whether to fire, never which skills are visible. |
CDN-served install.sh |
GitHub raw / git clone | The installer ships in the npm tarball and is served via jsdelivr, so curl … | sh works with a private source repo — no GitHub access, no auth, no clone. POSIX, Node-gated, no sudo/eval/nested-pipe. |
| Read-only Sleepy chat, enforced three ways | A single --disallowedTools flag |
Headless claude -p has no human to approve actions, so one flag isn't enough. --permission-mode plan blocks every mutating tool (including unknown MCP writes), --disallowedTools strips orchestration so a project's own SessionStart nag can't turn a Q&A into a maintenance flow, and a guard system prompt keeps the transcript on-topic. |
| Server-side Sleepy session persistence | Browser localStorage
|
The desktop app picks a fresh loopback port per launch, which wipes localStorage. Persisting the --resume session id + transcript per-vault in the state dir lets a conversation survive app restarts. |
| SSE for Sleepy chat | Request/response polling |
claude -p --output-format stream-json emits thinking/tool/text tokens incrementally; Server-Sent Events stream them to the dashboard as they arrive, no client poll loop. |
| Split board persistence (shared vs local) | One board.json, or browser localStorage
|
Saved views, the version list, and card properties must survive a fresh desktop loopback origin and travel with the repo. "Save for all" → version-controlled overrides/board.json; "save for yourself" → git-ignored state/board.local.json; the client merges them. localStorage would lose everything on every per-launch port change. |
- Why It Exists
- The Problem in Depth
- The Architecture
- The Hook Mechanism
- The Sleep Cycle
- Neuroscience-Inspired Memory
- The Dashboard
- Project task overrides
- Council Debates
- Memory Recall (BM25 over the curated corpus)
- Lab (Insights)
- Automations
- Federation
- Brain Cloud Sync
- Linked Repos
- Obsidian Integration
- CLI Design
- Install & Update
- The Desktop App
- Design Tradeoffs
- What Comes Next