File size: 10,112 Bytes
7596726
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# 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.md`
  Quick start, concepts, and user-facing orientation.
- `WIREFRAME.md`
  Architecture, execution flow, and file-map walkthrough.
- `docs/api-and-solver-policy.md`
  REST routes, lifecycle semantics, payload shape, and solver policy notes.
- `AGENTS.md`
  Rules for keeping code, comments, tests, and docs aligned in future changes.
- `Makefile`
  The shared developer command surface, including the local Space validation
  pipeline.
- `docs/screenshot.png`
  Current 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

- `Employee`
  Input data. The solver reads it, but does not move it.
- `Shift`
  The thing the solver is allowed to assign.
- `employee_idx`
  The one real decision variable in this app. It points from a shift to one
  employee inside `Plan.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:

1. `src/domain/employee.rs`
   Learn the input facts first.
2. `src/domain/care_hub.rs`
   Learn the hospital service-line grouping used by nearby search.
3. `src/domain/mod.rs`
   See the `planning_model!` manifest that lists and exports the model modules.
4. `src/domain/plan.rs`
   Learn the planning entity, planning variable, nearby meters, and derived
   fields.
5. `src/constraints/mod.rs`
   See the full score model at a glance.
6. `src/constraints/*.rs`
   Read one scheduling rule per file.
7. `src/data/data_seed/entrypoints.rs`
   See the public demo-data surface.
8. `src/data/data_seed/large.rs`
   See how the published dataset is assembled.
9. `src/solver/service.rs`
   See how a domain solve becomes a retained runtime job.
10. `src/api/routes.rs` and `src/api/sse.rs`
   See the HTTP contract.
11. `static/app/main.mjs`
    See the browser boot sequence.
12. `static/app/shell/` and `static/app/schedule/`
    See how stock `solverforge-ui` components are adapted to this hospital demo.

## Runtime Flow

The shortest way to understand the app is to follow one request all the way
through:

1. The browser loads `static/index.html`.
2. `static/app/main.mjs` loads config, the generated UI model, and the `LARGE`
   demo dataset.
3. The frontend turns the returned `PlanDto` into schedule rails and side-panel
   summaries.
4. When the user clicks Solve, the frontend sends the current plan to
   `POST /jobs`.
5. `src/api/routes.rs` converts that HTTP request into a `PlanDto`.
6. `PlanDto::to_domain()` rebuilds the in-memory `Plan`, including derived
   helper fields the solver expects.
7. `SolverService` starts a retained solve through `SolverManager<Plan>`.
8. The solver emits lifecycle, phase, telemetry, best-solution, and analysis
   events.
9. `src/solver/service.rs` converts those runtime events into the JSON event
   shapes expected by the UI.
10. The browser consumes those events over `/jobs/{id}/events` and 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

```text
.
β”œβ”€β”€ 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_insertion` builds a feasible first assignment
- local search stays in the nearby scalar neighborhood
- `late_acceptance` and `accepted_count` keep 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 panels
- `schedule/` 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:

1. Domain and constraint logic:
   `make test-rust`
2. Slow end-to-end solve quality:
   `make test-slow`
3. Frontend module correctness:
   `make test-frontend-syntax`
4. Frontend module behavior:
   `make test-frontend`
5. Served browser behavior:
   `make test-e2e`
6. 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.