A fork of mtelver's day10 project

Add web frontend design document

Design for day10-web, a status dashboard for package maintainers
and operators. Key decisions:

- Separate service reading day10's output directories
- OCaml with Dream web framework
- Server-rendered HTML with minimal JS
- Pages: Dashboard, Package list/detail, Run history/detail
- Dependency graph exploration (both directions)
- Public read-only, no authentication
- No database (filesystem as source of truth)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

+274
+274
docs/plans/2026-02-04-web-frontend-design.md
··· 1 + # day10-web: Status Dashboard Design 2 + 3 + **Date:** 2026-02-04 4 + **Status:** Approved 5 + **Author:** Brainstorming session 6 + 7 + ## Overview 8 + 9 + A web frontend for day10 that allows package maintainers to check their package status and operators to monitor system health. It runs as a separate service that reads day10's output directories. 10 + 11 + ## Audience 12 + 13 + 1. **Package maintainers** - Want to see if their packages are building/documented correctly, investigate failures 14 + 2. **day10 operators/admins** - Monitoring system health, viewing logs, managing runs 15 + 16 + Not intended as a general documentation browser (that's what the generated HTML at `/docs/` is for). 17 + 18 + ## Architecture 19 + 20 + ``` 21 + ┌─────────────┐ writes ┌──────────────────────────┐ 22 + │ day10 │ ───────────────►│ /data/ │ 23 + │ (batch) │ │ ├── cache/logs/ │ 24 + └─────────────┘ │ │ ├── runs/ │ 25 + │ │ │ └── summary.json│ 26 + │ │ └── latest │ 27 + │ └── html/ │ 28 + ┌─────────────┐ reads │ └── p/{pkg}/{ver}/ │ 29 + │ day10-web │ ◄───────────────┤ │ 30 + │ (Dream) │ └──────────────────────────┘ 31 + └─────────────┘ 32 + 33 + 34 + HTTP :8080 35 + ``` 36 + 37 + **Key properties:** 38 + - No database - all state derived from filesystem 39 + - Read-only access to day10's directories 40 + - Single configuration: paths to cache-dir and html-dir 41 + - Lightweight: `day10-web --cache-dir /data/cache --html-dir /data/html` 42 + 43 + ## Pages and Routes 44 + 45 + ### Dashboard (`/`) 46 + 47 + - Overview cards: total packages, build success rate, doc success rate 48 + - Latest run summary (timestamp, duration, pass/fail counts) 49 + - Link to full run history 50 + 51 + ### Package List (`/packages`) 52 + 53 + - Searchable/filterable table of all packages 54 + - Columns: package name, version, build status, doc status, last updated 55 + - Click through to package detail 56 + 57 + ### Package Detail (`/packages/{name}/{version}`) 58 + 59 + - Build status with link to build log 60 + - Doc status with link to doc log and generated docs 61 + - Dependencies tab: what this package depends on (with their statuses) 62 + - Reverse dependencies tab: what depends on this package 63 + - Solver solution: OCaml version, full dependency list with versions 64 + 65 + ### Run History (`/runs`) 66 + 67 + - List of all batch runs (timestamp, duration, success/fail counts) 68 + - Click through to run detail 69 + 70 + ### Run Detail (`/runs/{run-id}`) 71 + 72 + - Full summary.json data displayed nicely 73 + - List of failures with links to logs 74 + - Filterable list of all packages processed in that run 75 + 76 + ## Data Sources 77 + 78 + All data is read from the filesystem: 79 + 80 + ### Run data (`{cache-dir}/logs/`) 81 + 82 + | Path | Provides | 83 + |------|----------| 84 + | `runs/` directory listing | Run history | 85 + | `runs/{id}/summary.json` | Run statistics, failure list | 86 + | `runs/{id}/build/*.log` | Build logs | 87 + | `runs/{id}/docs/*.log` | Doc generation logs | 88 + | `latest` symlink | Most recent run | 89 + 90 + ### Package data (`{cache-dir}/{platform}/`) 91 + 92 + | Path | Provides | 93 + |------|----------| 94 + | `solutions/` | Cached solver results (deps, OCaml version) | 95 + | `build-*/layer.json` | Build metadata and status | 96 + | `doc-*/layer.json` | Doc generation metadata and status | 97 + 98 + ### Generated docs (`{html-dir}/`) 99 + 100 + | Path | Provides | 101 + |------|----------| 102 + | `p/{pkg}/{ver}/` existence | Doc generation succeeded | 103 + | Direct links | Link to generated documentation | 104 + 105 + ### Dependency graph 106 + 107 + - Built from solutions data 108 + - Forward deps: parse the solution for a package 109 + - Reverse deps: scan all solutions (indexed on startup) 110 + 111 + ## UI Approach 112 + 113 + ### Rendering: Server-side HTML with minimal JS 114 + 115 + Dream renders HTML directly using its built-in HTML DSL or Tyxml. No heavy frontend framework: 116 + 117 + - HTML pages rendered on server 118 + - Small amount of vanilla JS for search/filtering 119 + - CSS styling (Pico CSS or simple custom styles) 120 + 121 + ### Why this approach 122 + 123 + - Simpler to build and maintain 124 + - No frontend build pipeline 125 + - Fast initial page loads 126 + - Works without JavaScript for core functionality 127 + - Fits "operational dashboard" use case 128 + 129 + ### Visual style 130 + 131 + - Clean, functional dashboard aesthetic 132 + - Status badges: green (success), red (failed), yellow (skipped) 133 + - Sortable tables for package lists 134 + - Collapsible sections for dependency trees 135 + - Syntax highlighting for logs (highlight.js) 136 + 137 + ### Log viewer 138 + 139 + - Display logs inline with scrolling 140 + - Link to raw log file for download 141 + - Client-side search within log 142 + 143 + ## Project Structure 144 + 145 + ``` 146 + /workspace/ 147 + ├── day10.opam # Existing - the batch runner 148 + ├── day10-web.opam # New - the web frontend 149 + ├── bin/ 150 + │ └── main.ml # Existing day10 CLI 151 + ├── lib/ # Existing day10_lib 152 + ├── web/ 153 + │ ├── dune 154 + │ ├── main.ml # day10-web entry point 155 + │ ├── server.ml # Dream routes and handlers 156 + │ ├── views/ 157 + │ │ ├── layout.ml # Common HTML layout 158 + │ │ ├── dashboard.ml # Dashboard page 159 + │ │ ├── packages.ml # Package list and detail pages 160 + │ │ └── runs.ml # Run history and detail pages 161 + │ ├── data/ 162 + │ │ ├── run_data.ml # Read summary.json, logs 163 + │ │ ├── package_data.ml # Read solutions, layer metadata 164 + │ │ └── deps.ml # Dependency graph builder 165 + │ └── static/ 166 + │ ├── style.css 167 + │ └── app.js # Minimal JS for search/filter 168 + └── dune-project # Update to add day10-web package 169 + ``` 170 + 171 + **Shared code:** `day10-web` depends on `day10_lib` to reuse types (e.g., `Run_log.summary`). 172 + 173 + ## CLI and Configuration 174 + 175 + ``` 176 + day10-web [OPTIONS] 177 + 178 + Required: 179 + --cache-dir DIR Path to day10's cache directory 180 + --html-dir DIR Path to generated documentation 181 + 182 + Optional: 183 + --port PORT HTTP port (default: 8080) 184 + --host HOST Bind address (default: 127.0.0.1) 185 + --platform PLATFORM Platform subdirectory (default: debian-12-x86_64) 186 + ``` 187 + 188 + ### Example usage 189 + 190 + ```bash 191 + # Development 192 + day10-web --cache-dir /data/cache --html-dir /data/html 193 + 194 + # Production (bind to all interfaces) 195 + day10-web --cache-dir /data/cache --html-dir /data/html \ 196 + --host 0.0.0.0 --port 80 197 + ``` 198 + 199 + ### Deployment with nginx 200 + 201 + ```nginx 202 + server { 203 + listen 80; 204 + server_name docs.example.com; 205 + 206 + # Status dashboard 207 + location / { 208 + proxy_pass http://127.0.0.1:8080; 209 + } 210 + 211 + # Generated documentation 212 + location /docs/ { 213 + alias /data/html/; 214 + autoindex on; 215 + } 216 + } 217 + ``` 218 + 219 + ## Error Handling 220 + 221 + ### Missing data 222 + 223 + | Condition | Behavior | 224 + |-----------|----------| 225 + | No runs yet | Dashboard shows "No runs recorded" | 226 + | Package not found | 404 with search suggestions | 227 + | Run ID not found | 404 with link to run history | 228 + | Log file missing | "Log not available" (may be GC'd) | 229 + | Malformed JSON | Log warning, show partial data | 230 + 231 + ### Large data sets 232 + 233 + | Data | Strategy | 234 + |------|----------| 235 + | Package list | Paginated (50/page) with search | 236 + | Run history | Paginated (20/page), most recent first | 237 + | Dependency tree | Depth-limited (2 levels), click to expand | 238 + | Reverse deps | Count with paginated list | 239 + 240 + ### Concurrent access 241 + 242 + - Read-only filesystem access is safe 243 + - Atomic swaps mean readers see consistent state 244 + - No locking needed 245 + 246 + ### Startup 247 + 248 + - Validate cache-dir and html-dir exist 249 + - Build reverse dependency index 250 + - Log startup time and index size 251 + 252 + ## Out of Scope (YAGNI) 253 + 254 + - Real-time updates / WebSockets 255 + - Authentication / access control 256 + - Write operations (triggering builds) 257 + - REST API (just HTML pages for now) 258 + 259 + ## Dependencies 260 + 261 + New opam dependencies for day10-web: 262 + - `dream` - Web framework 263 + - `tyxml` or Dream's HTML DSL - HTML generation 264 + 265 + ## Implementation Plan 266 + 267 + 1. Set up project structure (dune-project, day10-web.opam, web/ directory) 268 + 2. Implement data layer (run_data.ml, package_data.ml, deps.ml) 269 + 3. Implement views (layout, dashboard, packages, runs) 270 + 4. Wire up Dream routes in server.ml 271 + 5. Add static assets (CSS, minimal JS) 272 + 6. Add CLI with cmdliner 273 + 7. Update admin guide with deployment instructions 274 + 8. Write tests for data layer