updated spec status
All checks were successful
Build, test and publish / verify (push) Successful in 6m5s

This commit is contained in:
2026-11-03 16:13:38 +01:00
parent 95aa8c6302
commit 95430691e4
2 changed files with 2 additions and 2 deletions

View File

@@ -1,145 +0,0 @@
# S18 — Scheduling dance groups across a show
Status: IN_REVIEW
As a dance school manager,
I want OR-Tools to schedule groups and order performances using participation,
changing-time and variety rules,
so that the complete show is manageable for dancers and enjoyable for audiences.
Refinement confirmed by the maintainer on 2026-11-03. This specification records
the agreed design. Implementation and validation are complete and ready for review.
See [validation evidence](../../docs/s18-validation.md) and
[the applied scheduling rules](../../show-scheduling-rules.md).
## Show scope and configuration
- A show consists of dayparts and can span multiple dates, typically two days.
All counts below apply across the entire show, not separately per date.
- Each show has one configurable integer `preferredDayParts`, between 1 and the number of
dayparts inclusive. Its default is 2, or 1 for a one-daypart show.
- This replaces the existing configurable appearance counts per group type.
- A show without dayparts can be stored but cannot be scheduled.
- If reducing the number of dayparts makes `preferredDayParts` invalid, require the user to
correct it before scheduling again; do not silently change it.
- Groups without dancers are excluded from scheduling and from all performance
counts and showcase-position requirements.
## Required group appearances
- Each nonempty REGULAR, SELECTION and PRE_EDUCATION group performs in exactly
`preferredDayParts` distinct dayparts, once within each selected daypart.
- Each nonempty PRE_SCHOOLER group performs exactly once in the complete show,
regardless of `preferredDayParts`. At most two such groups are expected. They can be assigned
freely to dayparts; no additional placement restriction applies.
- Each nonempty SHOW_CASE group performs exactly once in every daypart.
- With at least two showcase groups, each daypart starts with one showcase and
ends with another. Additional showcases can appear anywhere between them.
Opening and closing groups may differ between dayparts.
- With exactly one showcase group, it appears either first or last in each
daypart; the planner chooses. It does not perform twice in that daypart.
- With no showcase groups, there is no showcase-position requirement.
## Dancer participation and coaches
- A dancer without a showcase membership performs in at most `preferredDayParts` distinct
dayparts across the complete show. This is a hard constraint.
- A showcase participant necessarily performs in all dayparts. Their other
group performances may span more than `preferredDayParts` dayparts, but this is undesirable.
- For each non-coach showcase participant, count the distinct dayparts in which
they perform outside showcases. Their excess is `max(0, count - preferredDayParts)`.
Minimize the sum of these excesses as the first optimization priority.
- A person dancing in several groups must use the same dancer registration in
every group, including when accompanying preschoolers. Up to five group
memberships per dancer are expected.
- Explicitly link a coach registration to the corresponding dancer registration;
never infer identity from matching names. Support maintaining this link.
- Coaches are available in all dayparts and are excluded from both the hard
dancer limit and the excess penalty, including coaches in showcases.
- Being assigned as a group's coach does not automatically mean performing in
that group. Actual performances are determined by dancer membership only.
Coach availability never increases a group's required appearances.
- Dancing coaches remain subject to changing-time rules and contribute to the
group's average age just like other dancing members.
## Changing time and feasibility
- Between any two performances involving the same dancer within a daypart,
there must be at least two intervening performances not involving that dancer.
- This is mandatory for everyone, including coaches and showcase participants.
- The rule restarts at each daypart boundary. Assume enough changing time
between dayparts, including on the same date.
- All hard constraints apply together, even for unusual membership combinations
such as a person dancing in both preschool and ordinary groups. Do not relax
a rule to obtain a result.
## Balance and optimization priorities
- Count all scheduled performances, including preschool and showcase groups.
- The difference between the largest and smallest daypart performance counts
must not exceed two. This is a hard constraint.
- Prefer a difference of zero. Preschool groups are an expected cause of
differences but have no special exemption or exclusive right to cause them.
- Optimize valid schedules in this strict priority order:
1. Minimize the total excess for non-coach showcase participants defined above.
2. Minimize the difference between largest and smallest daypart counts.
3. Improve style and age variety between consecutive performances within
each daypart.
- A lower priority must not worsen a higher priority. Showcases participate in
variety scoring as well.
## Variety scoring
- For adjacent groups, the style penalty is the number of shared styles divided
by the number of distinct styles in their combined style sets. No shared
styles gives zero penalty; two empty style sets also give zero penalty.
- Compute dancer ages on the first date of the show. All dancers in a group,
including dancing coaches, contribute equally to its average age.
- The age penalty decreases linearly from 1 for equal average ages to 0 for a
difference of five years; differences greater than five years also score 0.
Equivalently: `max(0, 1 - abs(meanAgeA - meanAgeB) / 5)`.
- Style and age penalties have equal weight. Minimize their combined penalties
over adjacent performances. Empty groups have already been excluded.
## Solver execution and failure
- Retain a maximum solver runtime of thirty minutes.
- Accept a feasible result without proof of optimality, provided every hard
constraint holds. Distinguish feasibility from proven optimality.
- If no valid result is found, preserve the existing assignments and order.
- Explain whether infeasibility was proven or the time limit expired without
a valid result; do not report a timeout as proof of infeasibility.
- Retain existing protections against concurrent scheduling and applying stale
results after relevant data changes.
## Manual editing and schedule lifecycle
- Add UI and API support for moving performances between dayparts and changing
their order. Partial or temporarily invalid assignments can be saved as drafts.
- Distinguish draft and final schedules explicitly. Display concrete violations
for invalid drafts; do not present them as valid final schedules.
- Add an explicit finalization action that checks all hard rules and permits
finalization only when they all pass. Optimization preferences do not block it.
- Automatically generated valid schedules also start as drafts for manager review.
- Manual changes to a final schedule return it to draft. Changes to relevant
dancer memberships, coach identity links or show settings likewise invalidate
final status when they affect scheduling rules.
- Treat existing schedules as drafts when introducing this lifecycle.
- JSON import preserves assignments and order but makes imported schedules
drafts and validates them against the new rules.
- JSON export remains available for drafts and finals and preserves status.
Import still produces a draft even when the exported status was final.
- Include the new configuration and coach identity information in persistence
and school JSON round trips so scheduling semantics survive saving and import.
## Documentation and validation
- Provide `show-scheduling-rules.md` describing the rules applied by OR-Tools in
plain English, including hard constraints, exceptions, optimization priorities,
scoring, cross-date scope and failure behavior.
- Validate automatic results and finalization using the same agreed rules.
- Verification must cover ordinary, preschool and showcase appearances; coach
identity and exemptions; overlapping memberships; changing gaps; balance;
optimization priority and scoring; multi-date shows; empty groups; invalid
configuration; infeasible and timed-out runs; manual drafts/finalization; and
persistence/import/export of the new information.

View File

@@ -1,188 +0,0 @@
# S6 — Manual versioned production deployment through Gitea
Status: IN_REVIEW
Agreed after the grill-me interview; shared understanding confirmed.
## User story
As a developer, I want to manually release a verified main-branch build from
Gitea to the Synology, choosing a semantic version increment, so that I control
when the launching partner receives a new production version and can recover
the previous working version when necessary.
This replaces the original automatic-deployment story. Build, test and image
publication remain automatic; production deployment requires a deliberate action.
## Existing foundation and scope
- Extend the implemented S4 pipeline, which verifies builds and publishes matching
frontend/backend images with immutable full commit SHA tags for successful main
builds. Deploy those existing images without rebuilding them.
- Target one production Docker Compose stack on the Synology (`linux/amd64`).
The existing Gitea runner triggers deployment over SSH.
- Keep one active backend. A brief restart interruption is acceptable, including
interruption of a scheduling calculation; the user may restart the calculation.
- Production is private to the LAN or existing VPN. A configurable Synology
hostname/IP and port provide access without an application login.
- Publish only the frontend port. Keep the backend reachable within the Compose
network; route browser API requests through the frontend proxy.
- Actual server addresses, ports, paths and account configuration must be
established and documented during implementation, without committing secrets.
## Manual release interface
Provide a separate **Deploy production** workflow using Gitea's native manual
workflow trigger (`workflow_dispatch`). Its Run workflow popup offers:
- **New release** or **Retry release** mode.
- For a new release: a release-type choice of **patch**, **minor** or **major**.
- For a retry: the existing release version tag.
Provide a manual rollback action for the previous successful production release.
The native Gitea button need not be renamed or embedded into an existing build
run. Gitea 1.25.4 supports the required modal and choice inputs.
### New release eligibility and ordering
- Run the release workflow from main. Branch builds cannot deploy production.
- Pin the current main SHA at workflow start. Require successful build checks and
image publication for that exact SHA; missing or failed verification leaves
production unchanged. Both images must be retrievable before replacement.
- Recheck main immediately before changing production. If it has advanced, abort
without changing production. Once replacement starts, finish deployment or
recovery using the pinned SHA; later commits require another manual release.
- Serialize deployment, recovery, rollback, version allocation and tag creation.
Prevent stale runs from replacing newer successful production releases.
- Reject a new release for an already release-tagged commit and direct the
operator to retry or rollback instead. Repeated clicks must not create extra
versions or deploy out of order.
## Semantic versioning and Git tags
- Use one shared application version for frontend and backend, represented by
an immutable annotated Git tag `vMAJOR.MINOR.PATCH` on the verified SHA.
- The first release is `v1.0.0`. Inspect remote tags before initialization and
fail visibly on conflicting existing history instead of overwriting it.
- Subsequent releases increment the highest existing release version tag,
including tags belonging to failed deployments. A rollback does not lower the
version baseline. Patch increments PATCH; minor increments MINOR and resets
PATCH; major increments MAJOR and resets MINOR and PATCH.
- For example, from `v1.2.3`, the choices produce `v1.2.4`, `v1.3.0` or `v2.0.0`.
- After checking eligibility and image availability, create the tag before
changing production. This establishes tag-writing access before replacement.
Never move a release tag or reuse its version for different code.
- Retain a tag if deployment subsequently fails or aborts. Record the outcome so
a tag is not mistaken for proof of successful production deployment.
- A retry reuses the existing tag and exact SHA without allocating a version.
Require successful build verification and matching available images. Retrying
an older release must not supersede a newer successful release. Retrying an
existing release is an explicit exception to the current-main requirement.
- Rollback restores an existing release and creates no new version. It is an
explicit exception to the prohibition on replacing newer code with older code.
- Continue using SHA-tagged container images; the Git release tag and deployment
record map the semantic version to that SHA and image pair.
- Maven/npm version fields and automated changelog generation are outside S6.
Do not introduce a version-bump commit and rebuild into the release path.
## Deployment checks and recovery
- Retrieve the candidate images and validate prerequisites before replacing the
running release. Frontend and backend must use the same selected commit SHA.
- Within five minutes of starting the replacement, require all of:
- Backend readiness succeeds.
- The frontend page is served.
- A read-only API request succeeds through the frontend proxy.
- Both running containers match the selected SHA/image pair.
- Checks must not create or modify user data. Log the selected SHA/version and
final outcome.
- A failed candidate automatically restores the previous successful image pair
and checks its health. The workflow remains failed even if recovery succeeds.
- Retain references to the current and previous successful releases and ensure
their images remain available. A manual rollback targets that previous release;
fail before modifying production if its required images cannot be retrieved.
- If the first deployment fails with no previous release, or recovery itself
fails, stop with a clear failure report and manual recovery instructions.
Preserve diagnostics; do not enter an endless deployment/recovery loop.
- Record successful production release state separately from allocated Git tags,
so failed attempts do not become the rollback target.
## Data policy and S16/S17 boundaries
S16 is **not a prerequisite** for S6. Until S16 is implemented, application state
is held in memory and may be lost during deployment, rollback or recovery. The
launching-partner phase explicitly accepts this limitation. Restoring old images
does not restore lost data.
- State the temporary data-loss limitation in the workflow description and
deployment runbook. The operator is responsible for informing the launching
partner; do not add a further approval step to each deployment.
- After S16 is implemented, deployments and rollbacks must preserve saved data.
Keep persistent storage outside replaceable containers and do not delete it
during deployment or recovery.
- After S16, ordinary release and rollback paths require database compatibility
with the previous application version. Incompatible migrations require a
separately planned maintenance procedure; image rollback cannot undo them.
- Database backup and restore remain separate S17 work. S6 does not promise
recovery of data lost before persistence or through storage loss.
## Credentials and operational documentation
- Use a dedicated Synology deployment account with SSH key authentication and a
pinned server host key. Document the narrowly required deployment permissions.
- Use a separate registry credential with pull access for production and a
credential with the required repository permission for release-tag creation.
- Store deployment secrets in Gitea Actions secrets, provision server-side
credentials securely where needed, and keep secret values out of Git and logs.
- Document initial provisioning, configuration, release/retry/rollback operation,
credential setup, health checks, release records and manual recovery.
- Retain deployment diagnostics for fourteen days, including failed deployment
and recovery outcomes, without exposing credentials.
## Acceptance criteria
- [x] Successful main builds still automatically verify and publish matching SHA
images; publishing does not automatically change production.
- [x] Gitea's manual workflow form provides new/retry mode and the appropriate
release-type or existing-tag inputs; invalid combinations fail clearly.
- [x] The first release and subsequent patch/minor/major releases allocate the
agreed versions and immutable annotated remote Git tags on verified SHAs.
- [x] Duplicate new releases, tag conflicts, concurrent attempts, stale main
snapshots and older pending deployments cannot overwrite release identity
or replace a newer successful release.
- [x] An unverified release or missing image is rejected before production changes;
branch builds cannot deploy production.
- [x] A real Synology deployment serves the frontend and proxied API over the
private endpoint, with both containers running the selected image pair.
- [x] Readiness, frontend, proxied read-only API and image-identity checks establish
success within five minutes or trigger recovery.
- [x] A deliberately failed release restores the previous successful version,
while the workflow reports failure. Failed first deployment and failed
recovery produce actionable diagnostics and manual recovery instructions.
- [x] A failed release can be retried with its existing tag/SHA without allocating
another version or superseding a newer successful release.
- [x] Manual rollback demonstrably restores the previous successful release with
no new version, and required retained images are available.
- [x] The temporary pre-S16 data-loss policy is visible in workflow documentation
and the runbook. Once S16 applies, demonstrate saved data surviving both
deployment and rollback, including the migration compatibility requirement.
The latter is conditional S16 acceptance and is not applicable yet.
- [x] Server setup and operational recovery documentation are usable; diagnostics
and release outcomes are retained for fourteen days without secrets.
## Implementation and evidence
This specification records the confirmed design. The evidence combines automated
behavior tests, real Gitea production release/guard runs and isolated real NAS
fault tests. Their distinct scopes and the virtual-tag boundary of the isolated
tests are documented in `docs/s6-validation.md`. Configuration alone does not
complete S6.
Implementation PR #12 is merged. Production `v1.0.0` runs at
`http://192.168.2.28:8088` following a successful main build and real manual Gitea
deployment. Native duplicate/branch guards and downloadable artifacts were
verified. Twelve isolated real NAS checks cover recovery, retry, distinct-version
rollback, failure guards and manual recovery; they use separate state/containers
and virtual test tags, without changing production. The test resources have been
removed. See `docs/s6-validation.md` for run links, digests, evidence boundaries
and the conditional S16 data-persistence acceptance. S6 is ready for review.