# SciMesh: plan for the initial web interface ## 1. Purpose and outcome Build a small, local-first web interface for manually checking the complete SciMesh pipeline. A person should be able to open one address, upload a small ChEMBL-style TSV, configure a supported run, observe workers and task progress, inspect failures, and download artifacts without composing raw HTTP requests. This is not a public multi-tenant product. It is an operator and demo interface for a trusted local team. The coordinator remains the only process with direct database and artifact-storage access; the browser never calls PostgreSQL and never receives a worker bearer token. The first release must be useful before CTX-07--CTX-10 are complete. Therefore it has two visibly different modes: | Mode | What it proves | What it must not claim | | --- | --- | --- | | **Pipeline check** | Upload, task creation, claim, heartbeat, artifact upload, task completion, retries, and downloads work end-to-end. | That multiple shard results have been scientifically reduced into one answer. | | **Final run** | A reducer has produced a durable final CSV for the full job. | Available only after CTX-09, and for graph only after CTX-10. | Never label a partial artifact as a final molecular result. The UI must show a clear `Pipeline check — partial results` badge while a reducer is unavailable. ## 2. Constraints and decisions - Serve the UI from the Go coordinator at the same origin. No React, Vue, Node build, CDN, or separate frontend service. - Use Go `html/template`, `embed`, ordinary CSS, and small vanilla JavaScript modules. The page works locally with `docker compose up`. - Keep worker APIs and UI APIs separate. The UI handlers call Go use cases; they do not make HTTP calls to worker endpoints. - Add a distinct `UI_AUTH_TOKEN` for browser/operator access. It must never reuse `WORKER_AUTH_TOKEN`, appear in page HTML, localStorage, logs, URLs, or error messages. For the initial local UI use HTTP Basic Auth over a trusted local/reverse-proxied connection. If `UI_AUTH_TOKEN` is unset, `/ui` and `/ui/api/*` return `404` and the coordinator stays API-only. - Keep all user-controllable text escaped by `html/template`; JavaScript renders API fields through `textContent`, never `innerHTML`. - All downloads go through coordinator-owned UI endpoints with authorization. Do not expose filesystem paths, `storage_key`, database errors, worker tokens, or raw worker tracebacks. - The API vocabulary should be canonicalised before UI forms are implemented. Choose one external workload spelling (`similarity-search` and `similarity-graph` recommended, matching the CLI) and keep legacy underscore aliases only at the worker boundary. Update `docs/api-contract.md` and `docs/openapi.yaml` in the same change. ## 3. Target user journey 1. Start coordinator/PostgreSQL and one or more `scimesh-worker` processes. 2. Open `http://localhost:8080/ui`, authenticate with the UI token, and see readiness plus the registered-worker table. 3. Choose **New run**, select a workload, fill validated parameters, choose a TSV, and submit it. 4. The UI redirects to `/ui/jobs/{job_id}` and polls every two seconds. 5. The operator sees counters, per-task attempt/lease/error state, and worker activity. They may copy the worker launch command but cannot start arbitrary processes from the browser. 6. During a pipeline check, download an input shard or partial CSV to validate manually. For a final run, download the final CSV only when the job status is `completed` and a final artifact exists. 7. A failed run exposes only its sanitized failure reason and retry state. The browser offers a safe retry action only after an explicit future API supports it; v1 never fabricates a retry by changing task rows directly. ## 4. What exists today and required gaps | Capability | Current state | UI plan | | --- | --- | --- | | Upload/chunk dataset | `POST /jobs/upload` exists | Reuse through a UI handler with server-side multipart validation. | | Aggregate status | `GET /jobs/{id}` exists | Add list/detail read models for the UI. | | Worker registration/lease flow | Implemented | Add a read-only worker list; no browser worker controls. | | Task diagnostics | No public list/detail response | Add sanitized job task list with attempt, status, lease owner, expiry and error. | | Artifact download | Worker endpoint exists | Add UI-authorized, job-scoped download proxy. | | Final result | Reducer is not implemented | Gate behind CTX-09; show partial diagnostic artifacts meanwhile. | | Distributed graph correctness | Planner/reducer unavailable | Do not advertise a multi-shard graph as final until CTX-10. | ## 5. Proposed structure ```text coordinator/ web/ templates/ layout.html dashboard.html job_new.html job_detail.html error.html static/ app.css dashboard.js job-detail.js internal/ transport/http/ ui_handlers.go ui_dto.go ui_auth.go ui_handlers_test.go usecase/ ui.go # read-only DTO orchestration, no HTML domain/ ui.go # only if a shared value object is genuinely needed storage/postgres/ ui_read_repo.go # parameterized listing/detail queries ``` Embed `web/templates` and `web/static` into the coordinator binary with `go:embed`. No assets are generated at runtime; `go test ./...` must work without Node/npm. ## 6. UI surface and routes ### HTML routes | Route | Purpose | Availability | | --- | --- | --- | | `GET /ui` | Dashboard: readiness, recent jobs, workers, quick actions. | WUI-03 | | `GET /ui/jobs/new` | New-run form and parameter help. | WUI-04 | | `GET /ui/jobs/{id}` | Job detail and polling shell. | WUI-03 | | `GET /ui/jobs/{id}/artifacts/{artifact_id}` | Authorized download proxy with attachment headers. | WUI-05 | ### JSON routes used only by the pages | Route | Response / action | Notes | | --- | --- | --- | | `GET /ui/api/overview` | readiness, recent jobs, workers | No secrets, no storage paths. | | `GET /ui/api/jobs` | cursor/page of compact job cards | Default 20, deterministic `created_at DESC, id DESC`. | | `POST /ui/api/jobs/upload` | validates form, streams dataset, returns `201 {job_id}` | Same input limits as `/jobs/upload`; form fields first, file last. | | `GET /ui/api/jobs/{id}` | job detail, counters, tasks, allowed artifacts | Polling endpoint, no raw DB model. | | `GET /ui/api/jobs/{id}/events` | **deferred** | Start with polling; no SSE/WebSocket in v1. | All `/ui` routes use UI authentication. Existing worker API routes keep worker authentication and are not relaxed for the browser. ## 7. Read models and data minimisation Create UI-specific DTOs; do not return domain/database entities directly. ```text JobCard: id, workload, created_at, status, total, pending, leased, running, completed, failed JobDetail: JobCard fields, parameters (allowlisted/redacted), tasks: [{id, chunk_index, status, attempt, max_attempts, lease_owner_display, lease_expires_at, error_code, error_message}], artifacts: [{id, kind, filename, size_bytes, sha256, downloadable}] WorkerCard: id, name, capabilities, status, last_heartbeat_at, created_at ``` Rules: - Display a shortened UUID by default but provide a copy button with the full value; never interpolate it into HTML. - Do not expose `storage_key`, absolute artifact paths, raw metrics containing unexpected values, auth configuration, or worker-local directories. - An artifact is downloadable only when it belongs to the requested job. A `final_result` is downloadable only after the job is `completed`; partial artifacts are marked diagnostic. - SQL uses explicit columns, pagination/cursors, deterministic ordering, and joins constrained by `job_id`. ## 8. Workload forms and validation ### 8.1 Common fields - TSV file, required, streamed; show expected columns `chembl_id` and `canonical_smiles`. - `chunk_rows`: integer 1--100000, default 1000. - optional human-readable run name is a later schema/API addition; v1 does not silently store it. - display file name and client-side size only as convenience; server limits and validation remain authoritative. ### 8.2 Similarity search Inputs: exactly one `query_smiles` or `query_id`, `top_k`, optional threshold, threshold direction, `max_rows`, and `progress_every`. For a runnable manual pipeline check before CTX-08, offer `query_smiles` and default `chunk_rows` large enough to create one shard. A `query_id` across multiple shards is disabled with an explanation until CTX-07 resolves it once before fan-out. The detail page calls an artifact a **partial top-k CSV**, not a global top-k, until CTX-09 reduction exists. ### 8.3 Similarity graph Inputs: threshold, threshold direction, block size, `max_rows`, and progress interval. The form may show a disabled **Experimental — not globally reduced** card, but it must not submit multi-shard graph jobs until CTX-10 implements block-pair planning and deterministic reduction. A one-shard pipeline check is allowed only behind an explicit acknowledgement and produces a diagnostic edge list, not a final graph. Validation exists in three places: HTML constraints for feedback, a small JavaScript schema for form behaviour, and authoritative Go validation mapped to typed workload parameters. Never pass arbitrary parameter maps straight from the browser to workers. ## 9. Delivery packages Each package is a separate PR/task context. Do not start a later package until its listed dependency and tests are green. ### WUI-00 — Freeze UI contract and demo scope **Depends on:** current `main`. **Deliver:** this plan, canonical workload naming decision, and updates to `docs/api-contract.md`/`docs/openapi.yaml` if names or statuses change. **Acceptance:** API has a precise distinction between diagnostic artifacts and final results; UI security model has a distinct token; CTX-07/09/10 limitations are visible. ### WUI-01 — UI read models and PostgreSQL queries **Depends on:** WUI-00. **Deliver:** repository/use-case methods for deterministic job lists, sanitized job details, task summaries, artifact metadata, and worker lists. Add indexes only if `EXPLAIN ANALYZE` on a realistic list query shows need. **Acceptance:** no N+1 query path; no storage key/secret leaks; unknown job is 404; artifact lookup is constrained to its job; Go unit plus real-PostgreSQL integration tests cover ordering, empty lists and ownership boundaries. ### WUI-02 — UI auth and embedded asset foundation **Depends on:** WUI-01. **Deliver:** `UI_AUTH_TOKEN` config validation, Basic Auth middleware, embedded template/static serving, security headers, and an API-only fallback when UI is disabled. **Acceptance:** worker token never authorizes `/ui`; UI token never authorizes worker routes; `/ui` is 404 when disabled; HTML/content-security headers are tested; no token appears in logs or errors. ### WUI-03 — Read-only dashboard and job detail **Depends on:** WUI-01, WUI-02. **Deliver:** dashboard, worker table, job list, detail page, two-second polling with pause when the tab is hidden, error/retry display, and accessible empty/ loading/error states. **Acceptance:** a manually created job changes from pending to running on the page without reload; all text is escaped; polling stops on terminal states; task attempts and sanitized errors are visible; handler/template tests cover XSS-shaped names and errors. ### WUI-04 — New-run upload form **Depends on:** WUI-02, WUI-03. **Deliver:** workload-specific form, typed Go validation, streamed upload, progress/submit state, redirect to job detail, and copyable worker launch instructions. **Acceptance:** invalid query combinations fail with 400 and clear UI feedback; valid small similarity-search upload creates deterministic shard count; upload limits are enforced; browser never sees worker auth; test covers malformed TSV, large/invalid fields, duplicate form fields, and failed coordinator storage. ### WUI-05 — Safe artifact downloads and diagnostic preview **Depends on:** WUI-01--04. **Deliver:** job-scoped download proxy, CSV preview limited by byte/row count, checksum/size metadata display, and prominent partial/final labels. **Acceptance:** artifact from another job is 404; path traversal cannot select a file; `Content-Disposition` is safe; preview never loads an unbounded CSV; no final-result button exists before CTX-09. ### WUI-06 — Final-result UX after CTX-09 **Depends on:** CTX-09 and WUI-05. **Deliver:** `reducing` state, final artifact card, final CSV preview/download, and deterministic result metadata. **Acceptance:** final link is shown only for `completed`; reducer failure is sanitized; page refresh/restart preserves final artifact; end-to-end test compares downloaded final search CSV with local reference output. ### WUI-07 — Full similarity-graph UX after CTX-10 **Depends on:** CTX-10 and WUI-06. **Deliver:** enabled graph form, block-pair planning summary, graph-specific progress, final edge-list preview/download, and warnings for low thresholds. **Acceptance:** graph result equals local brute force on a small fixture; no dense matrix is introduced; threshold direction is displayed and preserved; result order is deterministic. ### WUI-08 — Manual-demo script and CI browser checks **Depends on:** WUI-05; extend after WUI-06/07. **Deliver:** `make demo-ui` (or documented compose profile), a tiny tracked TSV fixture, start/stop instructions, and headless browser smoke tests. **Acceptance:** clean checkout can start coordinator, a worker, open UI, submit fixture, observe task completion, and download a diagnostic artifact. CI covers auth, upload, polling JSON, job isolation, and download permission. ## 10. Testing strategy | Layer | Required checks | | --- | --- | | Go domain/use case | status projection, artifact/job ownership, pagination ordering, redaction. | | Go HTTP | UI auth separation, malformed multipart, CSRF-safe same-origin policy, 404/401, headers, escaping. | | PostgreSQL | fresh migrations, list/detail query ordering, cross-job artifact denial, completed/final state. | | Browser | form validation, upload success/failure, polling transition, terminal state, safe text rendering. | | End-to-end | real PostgreSQL + coordinator + Python worker + small fixture; verify actual bytes/checksum. | Use Playwright only if it can run in CI without adding a production runtime dependency. Otherwise start with Go handler tests and a small `curl`/HTML smoke script, then add browser automation in WUI-08. ## 11. Manual verification script after WUI-05 ```sh # terminal 1 cd coordinator UI_AUTH_TOKEN='local-ui-secret' make up # terminal 2: use a separate worker token; do not paste it into the browser SCIMESH_COORDINATOR_URL=http://localhost:8080 \ SCIMESH_BEARER_TOKEN='worker-secret' \ scimesh-worker --work-dir ./worker-data # browser # http://localhost:8080/ui # authenticate with the UI token, upload a tiny TSV, then watch /ui/jobs/{id} ``` The actual configuration variable names, compose wiring, and launch command are implemented in WUI-02/WUI-08; this block is the acceptance target, not a claim that the interface exists today. ## 12. Explicit non-goals for the initial interface - no database browser or SQL console; - no browser-side RDKit or scientific calculation; - no worker start/stop shell execution from UI; - no multi-user accounts, RBAC, password reset, public internet exposure, or user-provided storage credentials; - no WebSocket/SSE, React/Vue, Docker requirement for the Python package, or deployment platform; - no claim that a distributed graph or search result is final before its planner/reducer acceptance criteria are met. ## 13. Definition of done for the first hand-testable release WUI-00 through WUI-05 are complete when a clean local checkout can run a trusted, authenticated local UI; display coordinator readiness, workers, jobs, tasks and safe errors; submit a valid small search pipeline check; poll it to a terminal task state; and download/preview the coordinator-owned partial CSV. The page must make the absence of final reduction impossible to miss.