refinement of spec S9 and updated S2 as implemented #5

Merged
wanjakrah merged 1 commits from refinement into main 2026-10-30 11:50:37 +01:00
6 changed files with 154 additions and 1 deletions

View File

View File

@@ -1,6 +1,6 @@
# S2 — ESLint for the frontend
Status: IN_REVIEW
Status: IMPLEMENTED
Agreed after the grill-me review; shared understanding confirmed.

View File

@@ -0,0 +1,153 @@
# S9 — Schedule dance groups across show dayparts with OR-Tools
Status: REFINED
Agreed after the grill-me review; shared understanding confirmed.
## User story
As a dance-school maintainer, I want to assign my school's dance groups to a
show's dated dayparts, using configurable appearance counts per group type,
so that appearances are spread evenly across the show.
## Show configuration
- A show can have one to six dayparts across multiple dates. Each daypart has a
date and a type: MORNING, AFTERNOON or EVENING. A date/type combination must be
unique within a show. For example, Friday afternoon, Saturday afternoon and
Saturday evening are three distinct dayparts.
- An unscheduled show may have no dayparts while being configured.
- Daypart dates are the source of truth. Remove the separate editable show date.
Display the range from the earliest to the latest daypart date, or
"No dates configured" when there are none.
- Each show stores an editable appearance count for every dance-group type,
including types with no current groups. Remove the backend
`DanceGroupType.dayPartsRequired` property.
- Initialize counts when creating the show using the following defaults, where
N is the configured number of dayparts:
| Type | Default appearances |
| --- | --- |
| PRE_SCHOOLER | 1 |
| SHOW_CASE | N |
| PRE_EDUCATION | N |
| All remaining types | min(2, N) |
- PRE_SCHOOLER's count is editable; one is a default, not a fixed exception.
- Both frontend and backend enforce integer counts greater than zero and no
greater than N for a configured show. With one daypart, all fields display one
and are disabled in the frontend.
- A show without dayparts remains unscheduled; usable counts must be initialized
when its initial daypart configuration becomes available.
- Subsequent daypart edits preserve the saved counts rather than reapplying
defaults. Block saving if a count exceeds the new number of dayparts until
the user corrects it. Adding a daypart does not automatically increase counts.
## Scheduling rules and objective
- Use Google OR-Tools in the backend.
- Include every current dance group belonging to the show's school.
- Each group must appear in exactly the number of distinct dayparts configured
for its type. A group cannot appear twice within one daypart.
- Among assignments satisfying those requirements, minimize the difference
between the largest and smallest number of group appearances per daypart.
Count every configured daypart, including an empty one.
- Assign groups to dayparts only. Do not calculate performance order or timing.
- Require at least one school group and one configured daypart. Disable the
scheduling action with an explanation when either is missing, and enforce
the same requirements in the backend.
## Scheduling action and result handling
- Provide an explicit "Schedule groups" action clearly indicating that it
replaces the current assignments. A separate preview/accept workflow is
outside this specification.
- Run scheduling synchronously, with a solver time limit of 30 minutes.
For S9, the frontend may remain blocked until the request returns; configure
the request path to accommodate the agreed duration.
- If a valid result is found, save it as a complete replacement of the existing
assignments. This includes a valid result whose optimality has not been proven
when the time limit is reached.
- Distinguish "optimal balance" from "valid result; optimal balance not proven"
in the result presented to the user.
- If validation fails, the problem is infeasible, the solver fails, or the limit
is reached without a valid result, explain the outcome and preserve existing
assignments. Never silently reduce required appearances.
- Disable the scheduling action while a run is pending. The backend must reject
another concurrent run for the same show.
- Before saving, verify that scheduling inputs have not changed during the run.
If they changed, reject the stale result, preserve existing assignments and
explain that the user must run scheduling again.
## Changes affecting existing schedules
- Before accepting a potentially invalidating edit, warn that an existing show
schedule may become invalid, name the affected shows and allow cancellation.
- Cover adding/deleting school groups, changing a group's type, changing show
dayparts and changing appearance counts when affected shows have assignments.
Name-only edits do not require this warning.
- After accepting the edit, do not automatically reschedule. Identify schedules
whose assignments no longer meet the appearance requirements and offer the
explicit scheduling action again.
- Newly added groups use their type's saved count on the next scheduling run.
## JSON import/export
- Preserve dated dayparts, group assignments and every per-type appearance
setting through JSON export/import.
- Update the format version and validation for the new structure, including
allowing the same daypart type on different dates.
- No legacy export compatibility or migration is required: the application is
still greenfield and no exports have been made.
## Optional repeatability
Identical inputs producing identical assignments is a nice-to-have for
testability, not a mandatory scheduling requirement. Spend at most three
minutes investigating how to achieve it. Implement it only if it requires at
most ten additional code statements; otherwise omit it. Changed inputs may
produce different assignments regardless.
## Out of scope and follow-up specifications
- Group exclusions: S10_allow-group-exclusion-for-show.
- Season context: S11_add-season-context-go-groups-and-shows.
- Asynchronous scheduling feedback: S12_support-async-feedback-for-scheduling.
- Scheduling on another PC: S13_create-runner-to-allow-scheduling-on-different-pc.
- Availability restrictions, pinned assignments, capacity limits, shared-dancer
constraints, duration/audience balancing and performance ordering.
S10–S13 are intentionally empty placeholders and are to be refined separately.
## Acceptance criteria
- [ ] Configure up to six dated dayparts, reject duplicate date/type combinations
and derive the displayed show date range from them.
- [ ] Persist editable per-type counts with the agreed defaults, remove the
backend type-level property and validate counts in frontend and backend.
- [ ] With one daypart, every count is one and frontend fields are disabled.
- [ ] Daypart edits preserve configured counts and require correction of counts
that exceed the new number of dayparts.
- [ ] OR-Tools assigns every school group to exactly its type's required number
of distinct dayparts and minimizes the busiest/quietest appearance gap.
- [ ] Scheduling requires groups and dayparts and replaces assignments only after
a successful, current result.
- [ ] The synchronous request supports a 30-minute solver limit, accepts valid
unproven results and communicates whether optimality was proven.
- [ ] Failed, infeasible, stale and no-result runs preserve existing assignments
and explain why no replacement was saved.
- [ ] Duplicate concurrent runs for a show are prevented in frontend and backend.
- [ ] Potentially invalidating edits warn beforehand, identify affected shows,
permit cancellation and expose invalid schedules afterward without
automatically rescheduling.
- [ ] New-format JSON round-trips dates, assignments and all type settings;
legacy import support is not required.
- [ ] Any repeatability work respects both the three-minute investigation limit
and the ten-additional-statement implementation limit.
## Implementation handoff
This specification records agreed behavior; it does not implement scheduling.
The current application uses in-memory storage with JSON import/export for
transfer. S9 adds the scheduling model, frontend controls, backend operation and
JSON changes within that existing persistence approach.