S12 - Async feedback for scheduling
All checks were successful
Build, test and publish / verify (push) Successful in 7m25s
All checks were successful
Build, test and publish / verify (push) Successful in 7m25s
This commit is contained in:
79
specs/S12-validation.md
Normal file
79
specs/S12-validation.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# S12 implementation validation
|
||||
|
||||
## Delivered behavior
|
||||
|
||||
Scheduling starts through a short POST returning 202 and a run identifier.
|
||||
The backend reserves and snapshots inputs before submitting background work;
|
||||
completion applies a validated result or records a distinct unsuccessful outcome.
|
||||
Per-show exclusion remains in the shared school service, with different shows
|
||||
allowed to calculate concurrently. Status publication and subscription snapshots
|
||||
share a lock so connecting cannot miss a transition between snapshot and subscription.
|
||||
|
||||
The solver publishes optimization phases and first feasible-solution discovery
|
||||
through a callback. Intermediate assignments remain private. Final summaries
|
||||
calculate the same attendance, spread and adjacency penalties as the solver.
|
||||
|
||||
SSE provides a current snapshot on connection and status changes, with separate
|
||||
15-second heartbeats. The nginx proxy disables buffering. The frontend keeps one
|
||||
application-wide connection, explicitly retries connection errors (including
|
||||
permanent EventSource failures caused by proxy responses), and reconciles state
|
||||
without event replay. Run identifiers deduplicate completion notifications.
|
||||
|
||||
## Validation
|
||||
|
||||
- Maven verify: 60 backend tests passed, plus 19 standards-checker regression tests.
|
||||
- Backend line coverage: 1349/1387, or 97.26%, above the required 80%.
|
||||
- Frontend: 29 unit tests, production build and ESLint.
|
||||
- Playwright: three real REST/browser scenarios, including scheduling completion,
|
||||
result summaries, a completion snackbar, reloading to recover the latest result
|
||||
and suppression of old notifications after reload.
|
||||
- HTTP SSE integration verifies initial snapshot, pushed completion and a new
|
||||
connection recovering the completed run.
|
||||
- Background tests cover duplicate exclusion, parallel shows, stale-result
|
||||
rejection, preserved assignments, failure outcomes and retry after failure.
|
||||
- Frontend tests cover missed completion after disconnect, reconnection, heartbeat
|
||||
handling, deduplication and completion arriving before the POST response.
|
||||
- Real solver tests cover phase/discovery callbacks, distinct infeasible/no-solution
|
||||
outcomes and objective-summary definitions including coach exemptions.
|
||||
|
||||
## Manual standards assessment
|
||||
|
||||
No waivers or weakened checks were introduced. The architecture tests pass.
|
||||
New records contain immutable values; the SSE snapshot defensively copies its
|
||||
list. The ten-component SchedulingRun record groups run identity, timestamps,
|
||||
progress and outcome in one immutable snapshot, avoiding mutable task state in
|
||||
API responses. Solver observers use the existing application-owned port and
|
||||
framework-independent domain types; OR-Tools remains encapsulated in solver.
|
||||
|
||||
Retained review signals:
|
||||
|
||||
- SchedulingRuns fan-out (25): task orchestration uses JDK executor/collection
|
||||
types, the scheduling port and immutable input/progress/result/status models.
|
||||
Its injected dependencies remain two; splitting these short lifecycle methods
|
||||
would distribute the shared publication and run-slot ordering.
|
||||
- SchedulingEventsResource fan-out (16): SSE/Mutiny transport, JSON envelope and
|
||||
service subscription types are confined to the REST adapter.
|
||||
- OrToolsSchedulingEngine fan-out (21), solve cognitive complexity (11) and
|
||||
length (40 code lines): the ordered three-stage loop retains deadline and
|
||||
earlier-feasible-result fallback behavior while publishing phase/discovery.
|
||||
requireSolution cyclomatic complexity (8) retains explicit mappings for stable
|
||||
failure outcomes and their human-readable explanations.
|
||||
- Integration/background test fan-out and the background lifecycle test's
|
||||
34-line method reflect real HTTP/solver fixtures and lifecycle assertions.
|
||||
Existing REST-test length signals remain below blocking limits after adapting
|
||||
them to await background completion.
|
||||
|
||||
## Persistence follow-up in S16
|
||||
|
||||
The maintainer explicitly transferred durable scheduling status and
|
||||
backend-restart recovery to S16 and approved completion of S12 with that scope
|
||||
adjustment. S12 is IN_REVIEW in `in_review/S12_support-async-feedback-for-scheduling`;
|
||||
implementation is ready for review and final acceptance is pending.
|
||||
|
||||
The application currently stores schools, saved assignments and the latest
|
||||
scheduling status in memory. S16 now explicitly requires PostgreSQL storage of
|
||||
accepted runs and their latest status, atomic durable result/status commits,
|
||||
stable identifiers in recovered API/SSE snapshots and startup conversion of
|
||||
previously running calculations to Interrupted without automatic resumption.
|
||||
Restart/crash recovery tests belong to S16; the S12 validation above covers
|
||||
navigation, browser reload and connection recovery within one backend lifetime.
|
||||
@@ -1,6 +1,6 @@
|
||||
# S12 — Asynchronous feedback for scheduling
|
||||
|
||||
Status: REFINED
|
||||
Status: IN_REVIEW
|
||||
|
||||
Agreed after the grill-me interview; shared understanding confirmed.
|
||||
|
||||
@@ -38,9 +38,9 @@ and what the outcome is, including when I leave and return to the show page.
|
||||
- For a successful result, show a concise summary with concrete results for
|
||||
attendance, spread and variety, using understandable labels.
|
||||
- Clearly distinguish invalid input, infeasibility, solver failure, time limit
|
||||
without a valid solution, rejection because inputs changed, and interruption
|
||||
by a backend restart. Explain why no result was applied and that existing
|
||||
assignments were preserved.
|
||||
without a valid solution and rejection because inputs changed. Explain why
|
||||
no result was applied and that existing assignments were preserved.
|
||||
Backend-restart interruption recovery belongs to S16.
|
||||
|
||||
## Applying results
|
||||
|
||||
@@ -85,16 +85,17 @@ and what the outcome is, including when I leave and return to the show page.
|
||||
|
||||
## Persistence and backend restarts
|
||||
|
||||
- Retain the latest scheduling status and outcome across backend restarts,
|
||||
aligned with the PostgreSQL persistence specified in
|
||||
`S16_store-state-in-persistent-storage`.
|
||||
- Calculations interrupted by a backend restart become recognizably Interrupted
|
||||
rather than remaining Running indefinitely. Do not resume them automatically;
|
||||
allow the user to start again.
|
||||
- Previously saved results survive restart. Volatile in-memory status alone
|
||||
does not satisfy S12; its implementation must integrate with S16 storage.
|
||||
- Target the single active backend instance defined by S16. Distributed
|
||||
execution and scheduling on another PC remain in S13.
|
||||
The maintainer explicitly transferred durable scheduling status and recovery
|
||||
following backend restarts to [S16](../refined/S16_store-state-in-persistent-storage)
|
||||
when completing S12. The latest status and outcome in S12 survive navigation,
|
||||
page reloads and SSE reconnection while the same backend process remains active.
|
||||
Schools, assignments and scheduling status currently remain in memory.
|
||||
|
||||
S16 owns PostgreSQL storage of the latest run, atomic persistence of saved
|
||||
assignments and completed status, and startup recovery of previously running
|
||||
calculations as Interrupted without automatic resumption. Previously saved
|
||||
results must survive restart under S16. These are not completion criteria for S12.
|
||||
Distributed execution and scheduling on another PC remain in S13.
|
||||
|
||||
## Out of scope
|
||||
|
||||
@@ -106,34 +107,38 @@ and what the outcome is, including when I leave and return to the show page.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [ ] Starting Schedule Groups returns without waiting for solver completion;
|
||||
- [x] Starting Schedule Groups returns without waiting for solver completion;
|
||||
leaving or reloading the page does not interrupt the calculation.
|
||||
- [ ] Opening a show in another screen recovers its current backend status;
|
||||
- [x] Opening a show in another screen recovers its current backend status;
|
||||
duplicate runs for the same show are prevented in frontend and backend,
|
||||
while different shows can run concurrently.
|
||||
- [ ] Running status shows elapsed time, phase and discovery of a valid solution
|
||||
- [x] Running status shows elapsed time, phase and discovery of a valid solution
|
||||
during a phase, without a fabricated completion percentage or ETA.
|
||||
- [ ] SSE delivers status changes promptly through the deployed proxy. Initial
|
||||
- [x] SSE delivers status changes promptly through the deployed proxy. Initial
|
||||
connection and reconnection reconcile the current state, and loss of
|
||||
connectivity visibly marks status as potentially outdated.
|
||||
- [ ] Successful runs automatically apply their result after the existing start
|
||||
- [x] Successful runs automatically apply their result after the existing start
|
||||
confirmation, retain stale-input protection and distinguish proven optimal
|
||||
results from valid results at the time limit.
|
||||
- [ ] The final display includes finish time and concrete attendance, spread and
|
||||
- [x] The final display includes finish time and concrete attendance, spread and
|
||||
variety results when successful; unsuccessful outcomes are distinguishable
|
||||
and explain that previous assignments were preserved.
|
||||
- [ ] An application-wide snackbar reports completion or failure with the show
|
||||
- [x] An application-wide snackbar reports completion or failure with the show
|
||||
name and View show action, including after navigation to another page.
|
||||
- [ ] Connection recovery notifies once for a missed completion; phase changes,
|
||||
- [x] Connection recovery notifies once for a missed completion; phase changes,
|
||||
repeated reconnection and reopening do not replay old snackbars.
|
||||
- [ ] The latest status and outcome survive navigation, reload and backend
|
||||
restart through persistent storage integrated with S16.
|
||||
- [ ] After backend restart, interrupted calculations are marked Interrupted,
|
||||
do not resume automatically and can be started again; saved results remain.
|
||||
- [x] The latest status and outcome survive navigation, page reload and SSE
|
||||
reconnection within the lifetime of the running backend. Durable storage
|
||||
and recovery after backend restart are explicitly transferred to S16.
|
||||
|
||||
## Implementation handoff
|
||||
## Review handoff
|
||||
|
||||
This specification records the agreed design and behavior. Backend and frontend
|
||||
changes, persistence integration, proxy configuration and validation are
|
||||
subsequent implementation work. S12 replaces S9's synchronous request behavior
|
||||
while retaining the existing scheduling and result-application protections.
|
||||
Background execution, solver progress callbacks, SSE, frontend status recovery,
|
||||
result summaries, proxy streaming configuration and application-wide completion
|
||||
notifications are implemented and verified. S12 replaces S9's synchronous request
|
||||
behavior while retaining scheduling and result-application protections.
|
||||
|
||||
See [S12 validation](../S12-validation.md) for verification and retained standards
|
||||
signals. Durable storage and backend-restart recovery are tracked in S16 and do
|
||||
not block review of this specification under the agreed scope adjustment.
|
||||
Implementation is ready for review; final acceptance is pending.
|
||||
@@ -69,6 +69,34 @@ JSON remains the import/export format, rather than the primary storage model.
|
||||
interrupted, rather than remain indefinitely marked as running. They do not
|
||||
resume automatically. Previously saved results remain available.
|
||||
|
||||
## Persistent scheduling status (transferred from S12)
|
||||
|
||||
The maintainer explicitly moved durable scheduling status and backend-restart
|
||||
recovery from S12 to S16 when completing S12. S12 supplies background execution,
|
||||
status snapshots, SSE and notifications within the lifetime of the backend.
|
||||
|
||||
- Persist the latest calculation per show in PostgreSQL, including its run
|
||||
identifier, school/show identity, start and finish timestamps, state, latest
|
||||
phase, whether a valid solution was found, outcome explanation and final
|
||||
attendance/spread/variety summary when available. A run history is not required.
|
||||
- Persist acceptance of a run before acknowledging its start. After restart or
|
||||
crash, an accepted calculation must remain identifiable even if it never
|
||||
produced a result.
|
||||
- Commit successful assignment replacement and its completed run status
|
||||
atomically. Publish success through the status API and SSE only after the
|
||||
transaction commits. A failed commit must not leave a successful status with
|
||||
missing assignments, or replaced assignments with an outdated running status.
|
||||
- On startup, mark calculations left Running by the previous backend instance
|
||||
as Interrupted before serving their recovered status. Explain that no new
|
||||
result was applied, retain previously saved assignments and allow a new run.
|
||||
Do not resume calculations automatically or retain their old run-slot guards.
|
||||
- Recover the latest status and outcome through S12's existing status endpoint
|
||||
and initial/reconnection SSE snapshots. Preserve stable run identifiers so
|
||||
reconnecting clients can deduplicate notifications; reopening the application
|
||||
must not replay old completion snackbars.
|
||||
- Verify recovery after both graceful restart and abrupt process termination,
|
||||
including a calculation running at termination and a completed saved result.
|
||||
|
||||
## Database failures and schema changes
|
||||
|
||||
- Refuse mutations while the database is unavailable and provide a clear failure
|
||||
@@ -103,8 +131,14 @@ for now. S16 does not promise recovery after loss of the database or its volume.
|
||||
another: the stale edit receives a clear conflict response, and the user
|
||||
can reload and retry.
|
||||
- [ ] Scheduling results based on outdated input revisions are rejected.
|
||||
- [ ] Interrupted calculations are recognizably interrupted after restart;
|
||||
previously saved results remain available.
|
||||
- [ ] The latest accepted scheduling run, its stable identifier, timestamps,
|
||||
progress, outcome and objective summary survive normal restart and crash
|
||||
and are returned through the status API and SSE snapshots.
|
||||
- [ ] Successful assignment replacement and completed run status commit
|
||||
atomically; success is published only after durable commit.
|
||||
- [ ] Calculations left Running become Interrupted before recovered status is
|
||||
served; previous assignments remain, no automatic resumption occurs and a
|
||||
new run can start. Reopening does not replay old completion snackbars.
|
||||
- [ ] Database unavailability prevents successful mutation acknowledgements;
|
||||
startup connection and migration failures are clearly reported.
|
||||
- [ ] An empty database can be initialized through Flyway and populated through
|
||||
|
||||
Reference in New Issue
Block a user