Files
SciMesh/docs/web-interface-plan.md
T
Emil 7547a30bde
coordinator / test (push) Waiting to run
Add job cancellation and dataset row limit
2026-07-23 23:14:25 +03:00

373 lines
16 KiB
Markdown

# 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.
- `max_rows`: optional positive integer. The coordinator creates shards only
from the first N data rows, so a user can test a large upload without
creating thousands of tasks. It does not truncate the stored source blob.
- 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.