Files
agiledancers/docs/validation/S16-validation.md
Wanja Krah c6a495a93c
All checks were successful
Build, test and publish / verify (push) Successful in 8m54s
S19: consume versioned engineering resources and move landscape documentation
2026-11-08 15:23:10 +01:00

183 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
> Historical validation evidence from application commit `bd898e92d22f6c412e671908770f4d89c8ae12bc`. Commands retain their original paths; current tooling is documented in README.md.
# S16 implementation validation
## Delivered behavior
PostgreSQL owns normalized school, dancer, coach, group, membership, style, show,
daypart, appearance and ordered-assignment tables, plus the latest scheduling run.
Flyway V1 creates the schema, persistent identifier sequence, cross-school-safe
foreign keys and row constraints. School keys, runtime identifiers, relationship
identity, coach links, draft assignments and finalized state survive reload.
Application persistence uses JPA/Hibernate ORM with mapped entities in
`repository.entity` and `EntityManager` repositories. Relationships, element
collections, daypart order and orphan removal are managed by Hibernate; domain
objects and API responses remain framework independent. The hand-written JDBC
query/update layer and row readers have been removed.
The HTTP interceptor runs each school operation in a short repeatable-read JTA
transaction under the existing domain monitor. Reads convert managed entities into
detached domain snapshots. Hibernate's school `@Version` checks optimistic changes;
a change stamp marks child-only edits so the school revision advances exactly once.
Missing or stale caller revisions conflict before applying edits, and stale detached
JPA entities also fail. Successful graph merges flush before commit, and status
publication remains after durable commit. A single school-data endpoint gives the
editor a consistent data/revision snapshot. Open editors retain their original
revision across background refreshes; conflict responses preserve their input and
require reload before retry. Replacement imports retain the target school ID and
check the revision returned by preview. Existing JSON import/export semantics remain.
Domain-to-view projection now uses MapStruct 1.6.3 in the REST layer. The generated
CDI `DomainViewMapper` covers schools, dancers, groups, coaches, dayparts and shows.
A shared ID conversion projects relationships to ordered ID lists; validation results
remain an explicit argument and preferred-daypart defaults delegate to the existing
application rule. View record constructors retain their defensive copies. Entity to
domain graph construction remains explicit in the persistence adapter.
`unmappedTargetPolicy = ERROR` rejects missing target fields at compilation. A compiler
probe with the actual mapper and an added unmapped SchoolView component confirms this
failure, without modifying maintained sources. MapStruct's generated implementation
is marked `lombok.Generated`, preserving the existing generated-code scope of coverage
and architecture checks; handwritten mapper helpers remain covered and checked.
Flyway remains the schema authority; Hibernate validates rather than generates DDL.
V1 is unchanged. V2 preserves existing rows, adds the aggregate change stamp and a
JPA-managed identifier counter seeded above previously allocated IDs, and defers
date/type uniqueness until commit so Hibernate can reorder retained dayparts safely.
Identifier allocation takes a pessimistic lock on the counter row. Coach/dancer and
group/coach associations use Hibernate composite join mappings to share the owning
school key while letting Hibernate update the linked member column directly.
Run acceptance checks the school revision and persists the run before launch or
acknowledgement. Acceptance itself does not change scheduling inputs. Background
computation holds neither a database transaction nor the domain monitor. Progress
commits before publication. Successful assignment replacement, school revision and
terminal run status commit together; publication callbacks run only after commit.
Deferred constraint failures roll back both assignments and status without
publishing success. Database failure never falls back to acknowledged memory-only
writes. Disconnected status subscribers cannot turn a committed result into a
solver failure.
Startup recovery marks persisted Running runs Interrupted before serving status,
retains their IDs, progress and previous saved assignments, and creates no old run
guards. The frontend recognizes Interrupted and retains S12's reconnect/reload
notification behavior. Timestamps use PostgreSQL microsecond precision consistently
in live and recovered snapshots.
## Verification
- Maven clean verify: all 72 backend tests and 19 standards-checker regression tests
pass. The JPA integration run also verified Flyway V1 and V2 and Hibernate schema
validation against an empty database, as well as an upgrade of the populated V1
test database.
- Backend line coverage: 1763/1827 (96.50%), above the 80% gate.
- Frontend: 32 unit tests, production build and ESLint pass (unchanged by the
MapStruct-only backend change).
- Generated-mapper CDI tests cover ordered relationship IDs, an unlinked coach,
nested assignment order, defensive copies, explicit validation messages and the
configured/default preferred-daypart rule. The compile-time unmapped-field probe
passes with the expected compiler error.
- Playwright: the three existing real REST/browser scenarios pass with PostgreSQL;
a fourth scenario verifies two screens editing the same revision, conflict
handling with retained form input, and successful reload/retry.
- PostgreSQL tests cover simultaneous edits (one succeeds, one conflicts), missing
revisions, relational graph recovery, IDs and ordered assignments, finalized
state, deferred commit failure without success publication, failed replacement
import rollback, stale scheduling inputs and interrupted-run recovery. JPA-specific
tests verify stale detached entities, exactly one aggregate version increment for
child-only edits, and reordered dayparts retaining their assignments.
- `tooling/persistence/restart_test.py` runs the packaged production backend against
an isolated persistent PostgreSQL container. It passes graceful restart and
SIGKILL interruption checks, verifies a completed saved result after every
restart, checks stable recovered IDs through HTTP and SSE, starts a new run after
interruption, rejects mutations during a database outage, and verifies startup
failure for an unreachable database and a deliberately failing Flyway migration.
- CI helper tests: 17 pass. Release and actual deployment-shell behavior tests:
32 pass. Shell syntax and Java/frontend formatting checks pass.
The packaged-process checks deliberately stop/restart only the named disposable
test database. Production deployment was not performed. Database provisioning and
transition instructions are in `docs/persistent-storage.md`; export every school
before stopping the old in-memory release. Production Compose retains its database
volume outside application replacement. CI supplies a separate disposable browser
database and removes its anonymous volume during cleanup.
## Manual standards assessment
No waivers or weaker gates were added. Architecture tests pass. Persistence entities
and their embeddable keys belong to the repository layer, while the scheduling
records remain in `model.scheduling`. Mutable classes, no-argument construction,
setters and replaceable collections are deliberate JPA lifecycle requirements;
Hibernate installs managed collection wrappers and hydrates fields. Composite key
values are assigned before persistence and are not mutated afterward. None of these
entities or lazy collections escapes into the solver or the API. Existing immutable
snapshot records still defensively copy mutable collections.
Retained review signals in changed code:
- Database fan-out 27 and transaction cyclomatic complexity 8: JPA/JTA lifecycle,
rollback and heuristic failures, optimistic-conflict classification, and committed
publication are handled in one transaction owner. Its two injected dependencies
remain below the review signal.
- SchoolWriter fan-out 17: one adapter builds and merges the school graph, removes
deleted-show statuses, checks JPA revisions and flushes changes. PeopleMapping,
GroupMapping and ShowMapping separate domain conversion from ORM lifecycle.
- Entity fan-out reflects explicit relationship and collection annotations:
DayPartEntity 22, GroupEntity 24, RunEntity 17, SchoolEntity 18 and ShowEntity 23.
Each maps one existing table/aggregate boundary; splitting fields merely to lower
annotation-driven fan-out would obscure the relational mappings. Entity collections
are mutable only within the persistence adapter and Hibernate session.
- SchoolTransactions fan-out 19 and request cyclomatic complexity 9: four collaborators
cover the detached domain monitor, transaction owner, loader and writer. The branch
structure distinguishes reads, run acceptance and domain mutations and maps storage
failures separately from optimistic conflicts. SchoolChange's four components
keep the revision and operation classification together.
- SchedulingRuns fan-out 31: it coordinates immutable input/progress/run/result
records, the solver port, durable lifecycle operations and subscriber callbacks;
its three injected collaborators remain below the dependency review signal.
- SchoolManagementService fan-out 38, 30 public methods, beginScheduling
cyclomatic complexity 8 and the existing five-parameter updateShow are retained:
aggregate use cases and validations remain together. Load installs detached state
and resets transient input counters under the existing monitor.
- REST resource fan-out is 18–28 (DanceSchoolResource 25): resources combine HTTP
annotations, use cases, request/view contracts and the injected projection mapper.
The consistent data endpoint assembles views inside the transaction. DomainViewMapper
fan-out 25 represents six explicit source/target projections plus the MapStruct
annotations and two small shared helpers. DomainViewMapperTest fan-out 22 exercises
the generated CDI mapper's API edge cases. SchoolView's six components include the revision;
SchoolDataView's five components represent one editor snapshot. ImportPreview's
seventh component identifies the target revision for replacement.
- PersistenceTest fan-out 47, seven injected fixtures, 335 code lines and methods of
41/33/42 lines: these cross real ORM, graph and transaction boundaries, including
detached-entity conflicts, commit-time failure, failed import, child versioning and
reordered assignments. The fixtures remain below all blocking limits. Existing
changed REST/SSE/background fixture metrics retain their S12/S7 rationale.
One active backend remains the supported deployment model. Global execution
capacity, distributed workers, database backup/restore and recovery after database
volume loss remain outside S16. S17 owns backup/restore.
## Review follow-up: database recovery during calculation
Terminal scheduling outcomes whose transaction fails remain pending in their worker.
The worker retries storage once per second without holding the school monitor between
attempts, retaining its run guard until the terminal status commits. Publication still
happens only after commit. Shutdown interrupts the wait; startup recovery remains
responsible for persisted Running statuses after a process restart. If the show was
removed during the outage, the pending outcome is discarded rather than recreating
its status. The finish method's cognitive complexity 12 and cyclomatic complexity 8 distinguish
retryable storage failures, removed shows and shutdown; these lifecycle decisions
stay in the run owner. SchedulingRuns fan-out is now 34 and its test fixture fan-out
35: the added storage exception, interruption handling and outage fixture belong to
this existing persistence lifecycle boundary.
The regression test simulates unavailable storage during result persistence, verifies
that no terminal status is published prematurely and duplicate starts remain rejected,
then restores storage without restarting the backend and checks the stable run ID,
persisted failure status and released guard. Public transaction/callback documentation
now specifies transaction ownership, callback timing and exception behavior.
Follow-up verification: clean Maven verify against PostgreSQL passes all 73 backend
tests and 19 standards-checker tests. Standards and the coverage gate pass;
backend line coverage is 1769/1841 (96.09%).