# Implementation Blueprint -- LLMHosts.com

## META
- purpose: Exact build order for AI coding agent. Machine-readable. No ambiguity.
- created: 2026-02-14
- phase_scope: Phase 1 (Weeks 1-6) -- complete, shippable product
- synced_with: system-architecture.md, all epics, all ADRs
- rule: Execute steps IN ORDER. Each step lists inputs, outputs, acceptance criteria.

---

## PHASE 1: FOUNDATION (Steps 1-8)

### Step 1: Project Skeleton
```
ACTION: Create Python package structure
FILES TO CREATE:
  pyproject.toml              -- package metadata, dependencies, extras [smart] [full]
  llmhost/__init__.py         -- version, package metadata
  llmhosts/cli.py              -- CLI entrypoint (click or typer)
  llmhosts/config.py           -- config loading (~/.llmhosts/config.toml)
  llmhost/constants.py        -- ports, paths, version constants
  tests/__init__.py
  tests/conftest.py           -- pytest fixtures
  Dockerfile                  -- multi-stage, [full] tier
  docker-compose.yml          -- dev stack
  .github/workflows/ci.yml   -- lint + test + build
  .gitignore                  -- Python, node_modules, .llmhost/
  README.md                   -- quick start, install tiers

DEPENDENCIES (core):
  fastapi>=0.115.0
  uvicorn[standard]>=0.32.0
  httpx>=0.28.0
  litellm>=1.55.0
  click>=8.1.0
  rich>=13.9.0
  textual>=0.90.0
  pydantic>=2.10.0
  cryptography>=44.0.0
  toml>=0.10.2
  aiosqlite>=0.20.0
  websockets>=14.0

DEPENDENCIES [smart] (extras):
  onnxruntime>=1.20.0
  numpy>=1.26.0
  faiss-cpu>=1.9.0
  tokenizers>=0.21.0

DEPENDENCIES [full] (extras):
  torch>=2.5.0
  sentence-transformers>=3.3.0
  faiss-gpu>=1.9.0
  transformers>=4.47.0

ACCEPTANCE:
  - pip install -e . succeeds
  - pip install -e ".[smart]" succeeds
  - pip install -e ".[full]" succeeds
  - llmhosts --version prints version
  - docker build succeeds
```

### Step 2: Configuration System
```
ACTION: Config file loading + CLI config commands
FILES TO CREATE:
  llmhosts/config.py           -- load/save ~/.llmhosts/config.toml
  llmhost/paths.py            -- XDG-compliant path resolution

CONFIG SCHEMA (~/.llmhosts/config.toml):
  [proxy]
  host = "0.0.0.0"
  port = 4000

  [router]
  mode = "rule"                # "rule" | "knn" | "hybrid" | "full"
  exploration_rate = 0.05      # 5% deliberate exploration
  knn_confidence_threshold = 0.85

  [cache]
  enabled = true
  delta = 0.01                 # vCache max false positive rate
  max_size_mb = 1000

  [dashboard]
  tui = true
  web = true

  [tunnel]
  auto_start = false

ACCEPTANCE:
  - Config created on first run with defaults
  - llmhost config show prints current config
  - llmhost config set router.mode hybrid works
```

### Step 3: Database Layer
```
ACTION: SQLite database with Alembic migrations
FILES TO CREATE:
  llmhost/db/__init__.py
  llmhost/db/engine.py         -- async SQLite connection (aiosqlite)
  llmhost/db/models.py         -- SQLAlchemy ORM models (all tables from data model)
  llmhost/db/migrations/       -- Alembic migration directory
  llmhost/db/migrations/env.py
  llmhost/db/migrations/versions/001_initial.py
  tests/test_db.py

SCHEMA: Exact SQL from system-architecture.md Section 5.2

ACCEPTANCE:
  - llmhost db upgrade creates all tables
  - All tables match schema definition
  - Indexes created
  - Tests pass for CRUD on each table
```

### Step 4: Discovery Module
```
ACTION: Detect Ollama, hardware, tunnel tools
FILES TO CREATE:
  llmhosts/discovery/__init__.py
  llmhosts/discovery/ollama.py   -- detect Ollama at localhost:11434, list models
  llmhosts/discovery/hardware.py -- GPU (nvidia-smi/rocm-smi), CPU, RAM, disk
  llmhosts/discovery/tunnel.py   -- detect tailscale, cloudflared binaries
  tests/test_discovery.py

ACCEPTANCE:
  - ollama.detect() returns {found: bool, models: [...], vram_used: int}
  - hardware.detect() returns {gpu: {...}, cpu: {...}, ram_mb: int, disk_free_mb: int}
  - tunnel.detect() returns {tailscale: bool, cloudflared: bool}
  - Results stored in hardware_profile table
  - Works on Linux, macOS. Windows WSL best-effort.
```

### Step 5: BYOK Key Management
```
ACTION: Encrypted API key storage
FILES TO CREATE:
  llmhost/keys/__init__.py
  llmhost/keys/manager.py      -- add, remove, list, validate keys
  llmhost/keys/crypto.py       -- Fernet encryption, machine-derived key
  tests/test_keys.py

CLI COMMANDS:
  llmhosts keys add openai sk-xxx     -- validate + encrypt + store
  llmhosts keys add anthropic sk-xxx
  llmhosts keys remove openai
  llmhosts keys list                   -- show providers (not keys)

ACCEPTANCE:
  - Keys encrypted at rest in SQLite (byok_keys table)
  - Validation: test API call to provider, catch invalid keys
  - llmhosts keys list shows provider + validated status
  - Keys decrypted only when needed for API call
```

### Step 6: LiteLLM Backend Integration
```
ACTION: Unified backend routing via LiteLLM
FILES TO CREATE:
  llmhost/backend/__init__.py
  llmhost/backend/litellm_adapter.py  -- configure LiteLLM with Ollama + BYOK
  llmhost/backend/model_registry.py   -- track available models (local + cloud)
  tests/test_backend.py

LOGIC:
  - On serve start: discover Ollama models -> register in LiteLLM
  - On key add: register cloud provider in LiteLLM
  - model_registry.list() returns all available models with metadata:
    {name, provider, local: bool, cost_per_1k_tokens, capabilities}

ACCEPTANCE:
  - LiteLLM routes to Ollama models correctly
  - LiteLLM routes to cloud APIs with BYOK keys
  - Model registry accurate after discovery + key adds
  - Streaming works for both local and cloud
```

### Step 7: FastAPI Proxy (Core)
```
ACTION: OpenAI + Anthropic compatible proxy
FILES TO CREATE:
  llmhosts/proxy/__init__.py
  llmhosts/proxy/app.py            -- FastAPI app, CORS, lifespan
  llmhosts/proxy/routes_openai.py  -- /v1/chat/completions, /v1/completions, /v1/embeddings, /v1/models
  llmhosts/proxy/routes_anthropic.py -- /v1/messages
  llmhosts/proxy/routes_internal.py  -- /health, /metrics, /internal/*
  llmhosts/proxy/models.py         -- Pydantic request/response models (both dialects)
  llmhosts/proxy/normalizer.py     -- convert between dialects and CanonicalRequest
  llmhosts/proxy/streaming.py      -- SSE streaming handler
  tests/test_proxy.py

API CONTRACTS: Exact spec from system-architecture.md Section 4

KEY BEHAVIOR:
  - Detect dialect from request path (/v1/chat/completions = OpenAI, /v1/messages = Anthropic)
  - Normalize to CanonicalRequest internally
  - Route through cache -> router -> backend
  - Normalize response back to original dialect
  - Include llmhost extension metadata in response
  - Streaming: SSE with proper chunked transfer encoding

ACCEPTANCE:
  - curl POST /v1/chat/completions with OpenAI format -> valid response
  - curl POST /v1/messages with Anthropic format -> valid response
  - Streaming works for both dialects
  - /v1/models returns all available models
  - /health returns 200
  - llmhost metadata included in responses
```

### Step 8: `llmhosts serve` Command
```
ACTION: Single command that starts everything
FILES TO MODIFY:
  llmhosts/cli.py               -- add serve command
FILES TO CREATE:
  llmhosts/server.py            -- orchestrate startup sequence

STARTUP SEQUENCE:
  1. Load config
  2. Initialize database (create if needed, run migrations)
  3. Run discovery (Ollama, hardware, tunnel tools)
  4. Load BYOK keys
  5. Configure LiteLLM backend
  6. Start FastAPI proxy on configured port
  7. Print first-run instructions (API endpoints, OPENAI_API_BASE hint)
  8. If TUI enabled: start TUI dashboard alongside proxy
  9. If web enabled: serve React SPA on /dashboard

ACCEPTANCE:
  - llmhosts serve starts proxy in <5 seconds
  - Prints discovered models, hardware, available features
  - Prints both API endpoint URLs
  - Ctrl+C gracefully shuts down
```

---

## PHASE 2: INTELLIGENCE (Steps 9-14)

### Step 9: Rule-Based Router (Tier 0)
```
ACTION: Deterministic routing for clear cases
FILES TO CREATE:
  llmhosts/router/__init__.py
  llmhosts/router/base.py          -- RoutingDecision dataclass, Router ABC
  llmhosts/router/rule_engine.py   -- Tier 0 rule-based router
  llmhosts/router/manager.py       -- orchestrates tier escalation
  tests/test_router_rules.py

RULES:
  1. Explicit model requested -> route to that model (if available)
  2. User preference match -> apply preference
  3. Model capability match -> code request + code model available -> local
  4. Cost heuristic -> simple request + local model available -> local
  5. Latency heuristic -> interactive request -> prefer low-latency backend
  6. Availability -> requested model offline -> fallback chain
  7. No match -> escalate to Tier 1 (if installed) or use default

ACCEPTANCE:
  - Rule engine handles 100% of requests in core install
  - Routing decisions logged to routing_decisions table
  - Fallback chain works when primary backend fails
  - User preferences from config respected
```

### Step 10: Cost Tracking + Metrics
```
ACTION: Per-request cost tracking with honest baselines
FILES TO CREATE:
  llmhost/metrics/__init__.py
  llmhost/metrics/cost_tracker.py   -- calculate per-request costs
  llmhost/metrics/aggregator.py     -- running totals, daily/monthly summaries
  llmhost/metrics/baseline.py       -- capture and compare user baseline
  tests/test_metrics.py

COST CALCULATION:
  - Local (Ollama/vLLM): $0.00
  - Cloud: actual token cost from provider pricing (litellm.completion_cost)
  - Estimated cloud: what this request WOULD cost on the cloud model
    the router would have chosen if local wasn't available
  - Saved: estimated_cloud - actual

BASELINE:
  - On first run, prompt: "How much do you spend on AI monthly? [$0-50 / $50-100 / $100-200 / $200+]"
  - Store in user_baseline table
  - Monthly comparison: "This month: $X actual. Without LLMHosts: estimated $Y."

ACCEPTANCE:
  - Every request logs cost to requests table
  - Running totals queryable by day/week/month
  - Baseline captured and used in comparisons
  - No strawman comparisons (uses estimated cloud, not OpenAI list price)
```

### Step 11: vCache -- Exact Hash (Tier 0)
```
ACTION: SHA-256 exact match cache
FILES TO CREATE:
  llmhosts/cache/__init__.py
  llmhosts/cache/normalizer.py     -- normalize queries for caching
  llmhosts/cache/hash_cache.py     -- Tier 0 exact hash lookup
  llmhosts/cache/manager.py        -- orchestrates cache tier checks
  tests/test_cache_hash.py

NORMALIZATION:
  - Lowercase
  - Strip leading/trailing whitespace
  - Collapse multiple whitespace to single space
  - Remove system prompt (cache on user message only)
  - SHA-256 of normalized string

ACCEPTANCE:
  - Identical queries return cached response in <5ms
  - Different queries never match (0% false positives)
  - Hit count and last_hit_at tracked
  - Cache respects max_size_mb config
  - llmhost cache stats shows hash tier stats
```

### Step 12: vCache -- Entity Namespace (Tier 1)
```
ACTION: Entity extraction + namespace isolation
FILES TO CREATE:
  llmhosts/cache/entity_extractor.py  -- lightweight NER
  llmhosts/cache/namespace.py         -- namespace key generation
  tests/test_cache_namespace.py

ENTITY EXTRACTION (lightweight, no ML):
  - Programming languages: regex match against known list (Python, JavaScript, etc.)
  - Frameworks: regex match (React, Django, FastAPI, etc.)
  - File types: .py, .js, .ts, .rs, etc.
  - Domain keywords: database, API, auth, security, etc.
  - Compose namespace key: "python|fastapi|api" sorted alphabetically

ACCEPTANCE:
  - "sort list in Python" -> namespace "python|sorting"
  - "sort list in JavaScript" -> namespace "javascript|sorting"
  - Different namespaces -> never cross-matched
  - Extraction completes in <10ms
```

### Step 13: vCache -- Adaptive Semantic (Tier 2) [smart tier]
```
ACTION: vCache verified semantic matching
FILES TO CREATE:
  llmhosts/cache/vcache.py           -- adaptive threshold semantic cache
  llmhosts/cache/embeddings.py       -- ONNX embedding generation
  llmhosts/cache/logistic.py         -- online logistic regression per embedding
  tests/test_cache_vcache.py

REQUIRES: pip install llmhosts[smart]

LOGIC:
  1. Embed query using all-MiniLM-L6-v2 via ONNX Runtime
  2. Filter to matching namespace from Tier 1
  3. Search FAISS index within namespace for nearest cached embeddings
  4. For each candidate: check if similarity > learned_threshold
     - learned_threshold is per-embedding, updated by online logistic regression
     - Regression learns from: true positives (user didn't complain) and
       false positives (user gave negative feedback or re-asked)
  5. If best match passes threshold AND confidence > (1 - delta):
     return cached response
  6. Else: cache miss

DELTA PARAMETER:
  - Default: 0.01 (1% max false positive rate)
  - Configurable: llmhost config set cache.delta 0.005
  - Formally guaranteed: actual FP rate <= delta (vCache theorem)

ACCEPTANCE:
  - Semantically similar queries within same namespace -> cache hit
  - Cross-namespace queries -> never match
  - False positive rate stays below configured delta
  - Adaptive thresholds converge after ~100 queries
  - llmhost cache stats shows per-tier hit rates and FP rate
```

### Step 14: kNN Router (Tier 1) [smart tier]
```
ACTION: kNN embedding router over FAISS
FILES TO CREATE:
  llmhosts/router/knn_router.py      -- Tier 1 kNN routing
  llmhosts/router/embeddings.py      -- shared ONNX embedding (reuse from cache)
  llmhosts/router/training.py        -- FAISS index building from training data
  tests/test_router_knn.py

REQUIRES: pip install llmhosts[smart]

LOGIC:
  1. Embed query (shared pipeline with vCache -- same model, same inference)
  2. FAISS nearest-K lookup (K=10) in router_training_data index
  3. Score each candidate on:
     - model_fit: how well did this model handle similar requests?
     - latency: backend latency (from metrics)
     - cost: backend cost tier
     - privacy: PII signal from entity extractor
     - cache_probability: likelihood of cache hit for this model
  4. Weighted score -> select best backend
  5. If top score confidence > 0.85 -> ROUTE
  6. If <= 0.85 -> escalate to Tier 2 (if available) or use Tier 0 fallback

EXPLORATION:
  - 5% of requests: override kNN decision, route to random alternative
  - Log as was_exploration=1 in router_training_data
  - Collect feedback -> counterfactual training data

ACCEPTANCE:
  - kNN routing latency <15ms on CPU
  - FAISS index retrainable from llmhost router retrain command
  - Exploration rate configurable
  - Confidence threshold configurable
```

---

## PHASE 3: DASHBOARD (Steps 15-18)

### Step 15: TUI Dashboard
```
ACTION: Rich terminal UI alongside proxy
FILES TO CREATE:
  llmhost/dashboard/__init__.py
  llmhost/dashboard/tui.py         -- textual app
  llmhost/dashboard/widgets.py     -- savings ticker, request log, cache stats
  tests/test_tui.py

LAYOUT:
  +-----------------------------------------------+
  | LLMHosts v0.1.0 | localhost:4000 | 3 models    |
  +-----------------------------------------------+
  | SAVINGS: $12.47 saved | This month: $47 total  |
  | Without LLMHosts: est. $130 | Saved: 64%        |
  +-----------------------------------------------+
  | RECENT REQUESTS                                |
  | 14:32 code completion -> local llama:8b [T1]   |
  |        cost: $0.00 | quality: sufficient       |
  | 14:31 complex refactor -> claude-3.5 [T2]      |
  |        cost: $0.04 | quality: required          |
  | 14:30 chat -> local mistral:7b [T0 rule]       |
  |        cost: $0.00 | cached (semantic)          |
  +-----------------------------------------------+
  | CACHE: 34% hit | exact: 8% | ns: 12% | sem:14% |
  | FP rate: 0.3% (target: <1%)                    |
  +-----------------------------------------------+
  | BACKENDS: ollama OK | openai OK | anthropic --  |
  +-----------------------------------------------+

ACCEPTANCE:
  - TUI updates in real-time as requests flow
  - Savings ticker increments within 1 second of each request
  - Routing decisions visible per request
  - Cache stats per tier visible
  - Backend health visible
```

### Step 16: Web Dashboard (React SPA)
```
ACTION: Localhost web dashboard
FILES TO CREATE:
  llmhost/dashboard/web/           -- React SPA (built, bundled with package)
  llmhost/dashboard/web/index.html
  llmhost/dashboard/web/src/App.tsx
  llmhost/dashboard/web/src/components/SavingsTicker.tsx
  llmhost/dashboard/web/src/components/RequestLog.tsx
  llmhost/dashboard/web/src/components/CacheStats.tsx
  llmhost/dashboard/web/src/components/BackendStatus.tsx
  llmhost/dashboard/web/src/components/BaselineCapture.tsx
  llmhost/dashboard/web/src/hooks/useWebSocket.ts
  llmhosts/proxy/routes_dashboard.py  -- serve SPA + WebSocket

TECH: React 18 + TypeScript + Tailwind CSS + shadcn/ui
BUILD: Bundled into package (npm run build -> static files served by FastAPI)
WEBSOCKET: /ws/dashboard streams real-time updates

ACCEPTANCE:
  - http://localhost:4000/dashboard loads React SPA
  - WebSocket connection provides real-time updates
  - Same data as TUI: savings, requests, cache, backends
  - BaselineCapture shown on first visit
  - Responsive design (usable on phone via tunnel)
```

### Step 17: `llmhosts doctor` Command
```
ACTION: Post-install verification
FILES TO CREATE:
  llmhost/doctor.py           -- comprehensive health check

CHECKS:
  1. Python version >= 3.10
  2. Installed tier detection (core / smart / full)
  3. Ollama reachable (localhost:11434)
  4. Ollama models available
  5. GPU detected (type, VRAM)
  6. BYOK keys configured + valid
  7. Proxy responds on configured port
  8. Cache operational (write + read test)
  9. Router tier available (based on installed tier)
  10. Tunnel tools detected (tailscale, cloudflared)
  11. Disk space sufficient

OUTPUT:
  [OK] Python 3.12.0
  [OK] Installed tier: smart
  [OK] Ollama: 3 models (llama3.2:8b, codellama:7b, mistral:7b)
  [OK] GPU: NVIDIA RTX 4070 (12GB VRAM)
  [--] Cloud keys: none configured (add with: llmhosts keys add openai)
  [OK] Proxy: responding on localhost:4000
  [OK] Cache: operational (hash + namespace + semantic)
  [OK] Router: rule + knn + modernbert
  [OK] Tunnel: tailscale detected (not active)
  [--] Suggestion: pip install llmhosts[full] for Qwen-0.5B reasoning router

ACCEPTANCE:
  - All checks run in <10 seconds
  - Clear pass/fail/suggestion per component
  - Actionable fix suggestions for failures
```

### Step 18: `llmhosts cache` Commands
```
ACTION: Cache management CLI
FILES TO MODIFY:
  llmhosts/cli.py              -- add cache subcommands

COMMANDS:
  llmhost cache stats          -- per-tier hit rates, FP rate, size
  llmhost cache clear          -- clear all
  llmhost cache clear --namespace python  -- clear specific namespace
  llmhost cache config delta 0.005       -- set max FP rate

ACCEPTANCE:
  - Stats show: exact hits, namespace hits, semantic hits, FP rate, total size
  - Clear works globally and per-namespace
  - Delta change takes effect immediately
```

---

## PHASE 4: RELEASE (Steps 19-22)

### Step 19: Model Suggestion Engine
```
ACTION: Hardware-aware model recommendations
FILES TO CREATE:
  llmhost/suggest.py           -- model recommendations based on hardware

LOGIC:
  - Read hardware_profile
  - Match VRAM to recommended models:
    8GB: llama3.2:3b, codellama:7b, mistral:7b
    12GB: llama3.2:8b, codellama:13b, deepseek-coder:6.7b
    24GB: llama3.2:13b, codellama:34b, mixtral:8x7b
  - Check what's already installed (Ollama API)
  - Suggest missing models with ollama pull commands
  - Suggest tier upgrade if on core: "pip install llmhosts[smart]"

ACCEPTANCE:
  - llmhosts suggest-models shows personalized recommendations
  - Recommendations match hardware capabilities
  - Tier upgrade suggested when appropriate
```

### Step 20: CI/CD Pipeline
```
ACTION: GitHub Actions for automated testing and publishing
FILES TO CREATE/MODIFY:
  .github/workflows/ci.yml       -- lint + test on PR
  .github/workflows/publish.yml  -- publish to PyPI + Docker Hub on tag

CI PIPELINE:
  - Trigger: push to main, PR
  - Matrix: Python 3.10, 3.11, 3.12 x Linux, macOS
  - Steps: lint (ruff), type check (mypy), test (pytest), build
  - Test all three install tiers

PUBLISH PIPELINE:
  - Trigger: git tag v*
  - Steps: build sdist + wheel, publish to PyPI, build Docker, push to Docker Hub

ACCEPTANCE:
  - CI passes on all matrix combinations
  - PyPI publish works (test.pypi.org first)
  - Docker image builds and runs correctly
```

### Step 21: Documentation
```
ACTION: README, quickstart, integration guides
FILES TO CREATE:
  README.md                    -- complete README with quick start, all 3 tiers
  docs/guides/quickstart.md    -- detailed getting started
  docs/guides/integration-cursor.md    -- how to use with Cursor
  docs/guides/integration-claudecode.md -- how to use with Claude Code
  docs/guides/integration-aider.md     -- how to use with Aider

ACCEPTANCE:
  - README has: install, quick start, features, architecture overview
  - Integration guides tested with actual tools
  - All code examples verified working
```

### Step 22: Open Source Release
```
ACTION: Publish to GitHub, PyPI, Docker Hub
STEPS:
  1. Create GitHub repo (MIT license)
  2. Push all code
  3. Create GitHub release v0.1.0
  4. Publish to PyPI: pip install llmhosts
  5. Publish to Docker Hub: docker pull llmhost/llmhost
  6. Post announcement: HN, Reddit r/LocalLLaMA, Discord

ACCEPTANCE:
  - pip install llmhosts works from PyPI
  - docker run llmhost/llmhost works from Docker Hub
  - GitHub repo has README, LICENSE, CI badges
  - At least 1 integration guide tested end-to-end
```

---

## BUILD ORDER DEPENDENCY GRAPH

```
Step 1 (skeleton)
  -> Step 2 (config)
  -> Step 3 (database)
  -> Step 4 (discovery)
       -> Step 5 (keys)
       -> Step 6 (backend)
            -> Step 7 (proxy)
                 -> Step 8 (serve command)
                      -> Step 9 (rule router)
                      -> Step 10 (cost tracking)
                      -> Step 11 (hash cache)
                           -> Step 12 (entity namespace)
                                -> Step 13 (vCache) [smart]
                           -> Step 14 (kNN router) [smart]
                      -> Step 15 (TUI)
                      -> Step 16 (web dashboard)
                      -> Step 17 (doctor)
                      -> Step 18 (cache CLI)
                 -> Step 19 (suggest)
            -> Step 20 (CI/CD)
            -> Step 21 (docs)
                 -> Step 22 (release)
```

**Critical path**: 1 -> 2 -> 3 -> 4 -> 6 -> 7 -> 8 -> 9 -> 10 -> 15 -> 22

**Parallelizable after Step 8**:
- Steps 9, 10, 11 can run in parallel
- Steps 15, 16 can run in parallel
- Steps 13, 14 can run in parallel (both need [smart])
- Steps 19, 20, 21 can run in parallel

---

## ESTIMATED EFFORT

| Step | Effort | Cumulative |
|------|--------|-----------|
| 1-3 (skeleton, config, db) | 2 days | 2 days |
| 4-6 (discovery, keys, backend) | 2 days | 4 days |
| 7-8 (proxy, serve) | 3 days | 7 days |
| 9-10 (rule router, costs) | 2 days | 9 days |
| 11-12 (hash cache, namespace) | 2 days | 11 days |
| 13-14 (vCache, kNN) | 3 days | 14 days |
| 15-16 (TUI, web dashboard) | 4 days | 18 days |
| 17-18 (doctor, cache CLI) | 1 day | 19 days |
| 19-22 (suggest, CI, docs, release) | 3 days | 22 days |
| **Total Phase 1** | **~22 working days** | **~4.5 weeks** |

This leaves ~1.5 weeks buffer in the 6-week Phase 1 timeline for testing, polish, and the open source launch push.
