| --- |
| title: SolverForge Hospital |
| emoji: 🏥 |
| colorFrom: red |
| colorTo: pink |
| sdk: docker |
| app_port: 7860 |
| pinned: false |
| license: apache-2.0 |
| short_description: SolverForge hospital scheduling example |
| --- |
| |
| # SolverForge Hospital |
|
|
| `solverforge-hospital` is a beginner-friendly example of a real SolverForge app. |
| It answers one concrete question: |
|
|
| "Given a hospital workforce and a month of shifts, which employee should cover each shift?" |
|
|
| ## Documentation Map |
|
|
| Each top-level document has a different job: |
|
|
| - `README.md` |
| Quick start, concepts, API surface, and the shortest learning path. |
| - `WIREFRAME.md` |
| Architecture and request/data flow across backend, runtime, and frontend. |
| - `docs/api-and-solver-policy.md` |
| REST routes, payload shape, lifecycle semantics, and solver policy notes. |
| - `AGENTS.md` |
| Contribution rules, validation commands, and the documentation standard for future edits. |
| - `Makefile` |
| The beginner-friendly command surface for local development, validation, and |
| Docker-based Hugging Face Space preparation. |
|
|
| If you are new to SolverForge, read this repo as three layers: |
|
|
| 1. The planning-model manifest and model modules in `src/domain/` |
| 2. The score rules in `src/constraints/` |
| 3. The runtime and browser app in `src/api/`, `src/solver/`, and `static/app/` |
|
|
| ## What SolverForge Is Doing Here |
|
|
| SolverForge is the optimization engine. In this app: |
|
|
| - `Employee` is a problem fact: input data the solver does not move |
| - `Shift` is the planning entity: the thing the solver assigns |
| - `Shift.employee_idx` is the planning variable: the actual choice the solver makes |
| - the constraints decide whether an assignment is legal and whether it is good |
| - `solver.toml` tells the runtime how to search for a better assignment |
|
|
| The app ships one serious demo instance rather than many toy presets: |
|
|
| - fixed random seed |
| - fixed 28-day schedule horizon |
| - 50 employees |
| - 688 shifts |
| - 8-hour shifts |
| - deterministic generator |
| - retained-job runtime with pause, resume, cancel, snapshot, and analysis |
|
|
| ## Read The Code In This Order |
|
|
| If you want to learn the codebase, this order is the shortest path: |
|
|
| 1. [src/domain/employee.rs](src/domain/employee.rs) |
| `Employee` is the input fact model. |
| 2. [src/domain/care_hub.rs](src/domain/care_hub.rs) |
| `CareHub` explains how the app models service-line proximity. |
| 3. [src/domain/mod.rs](src/domain/mod.rs) |
| This is the `planning_model!` manifest that lists and exports the model modules. |
| 4. [src/domain/plan.rs](src/domain/plan.rs) |
| `Shift`, `Plan`, nearby search meters, and transport normalization live here. |
| 5. [src/constraints/mod.rs](src/constraints/mod.rs) |
| This lists every scoring rule. |
| 6. [src/constraints/*.rs](src/constraints) |
| Each file explains one real scheduling rule. |
| 7. [src/data/data_seed/entrypoints.rs](src/data/data_seed/entrypoints.rs) |
| This shows the public demo-data surface. |
| 8. [src/data/data_seed/large.rs](src/data/data_seed/large.rs) |
| This assembles the published benchmark instance. |
| 9. [src/solver/service.rs](src/solver/service.rs) |
| This is the retained-job runtime facade. |
| 10. [src/api/routes.rs](src/api/routes.rs) and [src/api/sse.rs](src/api/sse.rs) |
| These expose the HTTP and live-event contracts. |
| 11. [static/app/main.mjs](static/app/main.mjs) |
| This is the browser boot sequence. |
| 12. [static/app/shell/](static/app/shell) and [static/app/schedule/](static/app/schedule) |
| These adapt stock `solverforge-ui` pieces to the hospital example. |
|
|
| ## Project Shape |
|
|
| - `src/domain/` |
| `planning_model!` manifest, planning model, derived fields, nearby meters. |
| - `src/constraints/` |
| Hard and soft scoring rules. |
| - `src/data/` |
| Deterministic demo-data generator. |
| - `src/solver/` |
| Retained-job orchestration over `SolverManager<Plan>`. |
| - `src/api/` |
| REST routes, DTOs, and SSE endpoint. |
| - `static/app/` |
| Browser code built as plain ES modules on top of `solverforge-ui`. |
| - `tests/frontend/` |
| Browserless UI tests using the fake DOM in `tests/support/`. |
| - `tests/e2e/` |
| Playwright smoke tests against the served browser app. |
|
|
| ## Quick Start |
|
|
| This repo depends on the published SolverForge runtime and UI crates: |
|
|
| - `solverforge = 0.11.1` |
| - `solverforge-ui = 0.6.5` |
|
|
| Run the app: |
|
|
| ```sh |
| make run-release |
| ``` |
|
|
| Then open `http://localhost:7860`. |
|
|
| If you want to inspect the command surface first: |
|
|
| ```sh |
| make help |
| ``` |
|
|
| ## What You Will See |
|
|
| The browser UI does five things: |
|
|
| 1. Loads `static/sf-config.json` |
| 2. Loads `static/generated/ui-model.json` |
| 3. Fetches the `LARGE` demo dataset from `/demo-data/LARGE` |
| 4. Renders two schedule views: |
| `By location` and `By employee` |
| 5. Starts a retained solving job when you click Solve |
|
|
| The frontend is intentionally thin. It mostly uses stock `solverforge-ui` pieces: |
|
|
| - `SF.createBackend({ type: "axum" })` |
| - `SF.createHeader()` |
| - `SF.createStatusBar()` |
| - `SF.createSolver()` |
| - `SF.rail.createTimeline()` |
|
|
| The app also exposes a visible "REST API" guide inside the browser shell. See |
| [`docs/api-and-solver-policy.md`](docs/api-and-solver-policy.md) for the route |
| list, lifecycle semantics, payload shape, and solver policy notes that this |
| guide is expected to match. |
|
|
| ## Run Validation |
|
|
| Standard local validation: |
|
|
| ```sh |
| make test |
| ``` |
|
|
| Space-style local CI simulation: |
|
|
| ```sh |
| make ci-local |
| ``` |
|
|
| Slow acceptance solve: |
|
|
| ```sh |
| make test-slow |
| ``` |
|
|
| `make test` includes Rust tests, browserless frontend tests, and Playwright |
| browser tests. `make ci-local` is the main pre-push validation path for this |
| repo. It also checks formatting, runs clippy, builds the release binary, and |
| builds the Docker image that the Space-style deploy path expects. |
|
|
| ## Demo Dataset Intent |
|
|
| The generator is not random filler data. It is designed to publish a problem |
| that is: |
|
|
| - hard-feasible |
| - deterministic |
| - narrow enough that candidate choice matters |
| - rich enough that soft-score improvements still exist after construction |
|
|
| The hidden witness roster in `src/data/data_seed/witness.rs` is especially |
| important: it gives the generator an internal feasible assignment that is used |
| to shape unavailability and preferences, but it is never shown to the solver. |
|
|
| The `LARGE` dataset is built once and cached in memory because it is immutable |
| and deterministic. Each request still receives an owned `Plan`, but the server |
| does not regenerate the same public benchmark from scratch every time. |
|
|
| ## API And Solver Policy |
|
|
| Detailed REST route, payload, lifecycle, telemetry, and solver-policy reference |
| material lives in [`docs/api-and-solver-policy.md`](docs/api-and-solver-policy.md). |
| The runtime source of truth remains [solver.toml](./solver.toml). |
|
|
| ## Constraints |
|
|
| Hard constraints: |
|
|
| - Assigned shift |
| - Required skill |
| - Overlapping shift |
| - At least 10 hours between 2 shifts |
| - One shift per day |
| - Unavailable employee |
|
|
| Soft constraints: |
|
|
| - Undesired day for employee |
| - Desired day for employee |
| - Balance employee assignments |
|
|
| ## Docker |
|
|
| Build from this repository root: |
|
|
| ```sh |
| make space-build |
| make space-run |
| ``` |
|
|
| The Docker build uses the same published crates as local development. |
|
|