Spaces:
Sleeping
solverforge-hospital WIREFRAME
This file is the architectural map for beginners.
If README.md tells you how to run the app, this document tells you how the
pieces fit together and in which order to read them.
Documentation Roles
The docs in this repo are meant to work together rather than compete:
README.mdQuick start, concepts, and user-facing orientation.WIREFRAME.mdArchitecture, execution flow, and file-map walkthrough.docs/api-and-solver-policy.mdREST routes, lifecycle semantics, payload shape, and solver policy notes.AGENTS.mdRules for keeping code, comments, tests, and docs aligned in future changes.MakefileThe shared developer command surface, including the local Space validation pipeline.docs/screenshot.pngCurrent browser screenshot embedded by the README.
What This Repo Is Teaching
solverforge-hospital is a complete SolverForge example, not just a scoring
snippet.
It shows how to build a planning app where:
- the domain model is small and explicit
- the score rules are readable one file at a time
- the dataset is deterministic and intentionally shaped
- the solver runs as a retained background job
- the browser UI watches the solve through REST and SSE
The planning question is:
"For each hospital shift, which employee should be assigned?"
SolverForge Concepts In Plain Language
EmployeeInput data. The solver reads it, but does not move it.ShiftThe thing the solver is allowed to assign.employee_idxThe one real decision variable in this app. It points from a shift to one employee insidePlan.employees.- hard score Rules that must not be broken, such as missing skills or overlapping shifts.
- soft score Preferences and quality goals, such as honoring desired days or balancing workload.
- retained job A solve that keeps living in memory after it starts, so the UI can poll it, pause it, resume it, stop it through runtime cancel, or inspect snapshots. Delete is terminal cleanup before the next fresh Solve, not the Stop action.
Read Order
If you are new to this repo, read files in this order:
src/domain/employee.rsLearn the input facts first.src/domain/care_hub.rsLearn the hospital service-line grouping used by nearby search.src/domain/mod.rsSee theplanning_model!manifest that lists and exports the model modules.src/domain/plan.rsLearn the planning entity, planning variable, nearby meters, and derived fields.src/constraints/mod.rsSee the full score model at a glance.src/constraints/*.rsRead one scheduling rule per file.src/data/data_seed/entrypoints.rsSee the public demo-data surface.src/data/data_seed/large.rsSee how the published dataset is assembled.src/solver/service.rsSee how a domain solve becomes a retained runtime job.src/api/routes.rsandsrc/api/sse.rsSee the HTTP contract.static/app/main.mjsSee the browser boot sequence.static/app/shell/andstatic/app/schedule/See how stocksolverforge-uicomponents are adapted to this hospital demo.
Runtime Flow
The shortest way to understand the app is to follow one request all the way through:
- The browser loads
static/index.html. static/app/main.mjsloads config, the generated UI model, and theLARGEdemo dataset.- The frontend turns the returned
PlanDtointo schedule rails and side-panel summaries. - When the user clicks Solve, the frontend sends the current plan to
POST /jobs. src/api/routes.rsconverts that HTTP request into aPlanDto.PlanDto::to_domain()rebuilds the in-memoryPlan, including derived helper fields the solver expects.SolverServicestarts a retained solve throughSolverManager<Plan>.- The solver emits lifecycle, phase, telemetry, best-solution, and analysis events.
src/solver/service.rsconverts those runtime events into the JSON event shapes expected by the UI.- The browser consumes those events over
/jobs/{id}/eventsand updates the visible status, timeline, and analysis panels.
The browser shell also contains a visible REST API guide. That makes
static/app/shell/api-guide.mjs part of the documentation surface, not just a
UI helper.
File Map
.
βββ Cargo.toml
β Rust crate metadata and registry dependency requests.
βββ solver.toml
β Embedded solver policy. This is the runtime source of truth for search.
βββ solverforge.app.toml
β App metadata used by the surrounding SolverForge tooling.
βββ Dockerfile
β Container build for running the app outside the dev checkout.
βββ Makefile
β Local build, validation, and Docker/Space workflow wrapper.
βββ README.md
β Beginner run guide and learning path.
βββ docs/api-and-solver-policy.md
β REST, payload, lifecycle, telemetry, and solver-policy reference.
βββ WIREFRAME.md
β This architectural walkthrough.
βββ AGENTS.md
β Repo-specific contributor and documentation rules.
βββ docs/screenshot.png
β Current browser screenshot used by the README.
βββ src/
β βββ lib.rs
β β Crate root and public module surface.
β βββ main.rs
β β Axum server bootstrap, CORS, static serving, and route composition.
β βββ domain/
β β `planning_model!` manifest plus problem model modules.
β βββ constraints/
β β One scheduling rule per file plus the assembler in `mod.rs`.
β βββ data/
β β Deterministic demo-data generator and published entrypoints.
β βββ solver/
β β Retained-job facade over the SolverForge runtime.
β βββ api/
β DTOs, REST routes, and SSE streaming.
βββ static/
β βββ index.html
β β Browser entrypoint.
β βββ sf-config.json
β β Runtime UI config for the stock frontend shell.
β βββ generated/ui-model.json
β β Generated view metadata used by `solverforge-ui`.
β βββ app/
β βββ main.mjs
β β Browser boot and wiring.
β βββ shell/
β β App shell, state, solver controls, and panels.
β βββ schedule/
β β Hospital-specific grouping, presentation, and rail rendering.
β βββ views/registry.mjs
β Named view registration.
βββ tests/
βββ frontend/
β Browserless frontend tests.
βββ e2e/
β Playwright browser tests for the served app.
βββ support/
Fake DOM support used by the frontend tests.
Why The Model Looks This Way
This app is intentionally narrow.
- There is one planning entity type:
Shift. - There is one scalar planning variable:
employee_idx. - Nearby search is attached directly to that scalar variable.
- The solver does not juggle multiple variable types, chained assignments, or list planning.
That makes the example easier to learn because the optimization problem stays visible:
- facts live in
Plan.employees - decisions live in
Plan.shifts[*].employee_idx - score rules read those two things and judge the assignment
Why The Demo Data Is Structured
The demo-data generator is not filler.
It is designed to give beginners a problem that is:
- deterministic
- feasible
- interesting enough that local search still has work to do
- stable enough that tests and comparisons stay meaningful
One important design trick is the hidden witness roster in
src/data/data_seed/witness.rs.
That internal roster gives the generator a known feasible staffing pattern. The published dataset is then shaped around that witness, while the solver only sees the final public problem. This lets the repo ship a realistic-feeling demo without random feasibility failures.
Why The Runtime Uses REST And SSE
The solve is long-lived compared with a normal request/response handler, so the backend splits the contract into two parts:
- REST for control and snapshots
- SSE for live progress
That separation keeps the frontend simple:
- create a job
- poll or fetch details when needed
- subscribe once to the live event stream
It also mirrors how a real retained SolverForge app behaves in production.
One small but important detail: GET /jobs/{id} and GET /jobs/{id}/status
return the same summary payload. The second route exists as a stock-compatible
alias for clients that expect the explicit /status URL shape.
Solver Policy
solver.toml is embedded by Plan and is therefore part of the actual model,
not a side document.
The shipped search policy is deliberately conservative:
cheapest_insertionbuilds a feasible first assignment- local search stays in the nearby scalar neighborhood
late_acceptanceandaccepted_countkeep the search moving without blowing up step cost
That narrow configuration is intentional for this demo. It is the balanced 30-second policy that currently performs best on the hospital dataset.
Frontend Design
The frontend is intentionally thin.
This repo is not trying to teach a custom framework. It is showing how to take
stock solverforge-ui pieces and adapt them to one concrete planning problem.
shell/owns app lifecycle, backend wiring, and side panelsschedule/owns the hospital-specific transformation from domain data to visual rails- tests in
tests/frontend/lock browserless presentation behavior down - tests in
tests/e2e/verify the served browser app with Playwright
Validation Surfaces
When you change this repo, think in six separate layers:
- Domain and constraint logic:
make test-rust - Slow end-to-end solve quality:
make test-slow - Frontend module correctness:
make test-frontend-syntax - Frontend module behavior:
make test-frontend - Served browser behavior:
make test-e2e - Local deploy readiness for the Docker-based Space target:
make ci-local
If you keep those six layers green, the repo remains teachable and usable.