## ADDED Requirements

### Requirement: Stable canonical source identity and versioning
Timetabler SHALL identify each outbound Staff, Location, activity, and tombstone member with a stable Timetabler source identity/aggregate version namespaced by the configured integration deployment. Each finalized source change set SHALL additionally have one unique transaction ID, one positive globally commit-ordered source-scope sequence, one source-scope ordering key, a member count, a completeness marker, and one canonical transaction hash. The contract SHALL remain independent of Resource Booking's internal schema.

#### Scenario: Repeated update has a later version
- **WHEN** the same Staff, Location, or activity is committed in two successive authoritative changes
- **THEN** the second canonical event carries the same source identity and a strictly later aggregate/change version

#### Scenario: Adapter maps identity without RB data in Timetabler
- **WHEN** the Resource Booking-owned adapter consumes a canonical source record
- **THEN** it can maintain its RB identity mapping using the Timetabler identity and version without Timetabler storing an RB primary key or RB-shaped payload

### Requirement: Staff and Location lifecycle changes are atomic with the outbox
Every Staff and Location create, update, archive/status transition, and delete path SHALL insert its canonical lifecycle event in the same database transaction as the authoritative mutation. A transaction SHALL commit both records or neither record.

#### Scenario: Lifecycle commit succeeds
- **WHEN** a Staff or Location create/update/archive/delete transaction commits
- **THEN** exactly one lifecycle outbox record for that committed version is queryable with actor, correlation, and final state or tombstone

#### Scenario: Lifecycle transaction fails
- **WHEN** an exception is injected after the Staff or Location database mutation but before transaction completion
- **THEN** the lifecycle mutation and its outbox row are both absent and no external integration effect has run

#### Scenario: Bulk import updates resources
- **WHEN** `api/views/admin/import_table.py` creates or updates Staff or Locations in a bulk transaction
- **THEN** versioned lifecycle outbox records for every committed resource are created within that bulk transaction

### Requirement: Authoritative final allocation replacement events
Each committed final allocation change SHALL publish canonical replacement state, not an append-only allocation hint. The member SHALL contain final activity/allocation state plus old and new affected Staff IDs, Location IDs, week/calendar references, time/slot/duration data, deterministic local/UTC occurrence boundaries with stable occurrence/week references, and deletion/merge tombstones needed to remove stale mirror occurrences.

#### Scenario: Week pattern expands to absolute source facts
- **WHEN** a scheduled activity has a week set, local day/start time, duration, and configured IANA timezone
- **THEN** its final canonical state contains deterministic local-offset and UTC start/end facts for every week, including week start, occurrence reference, UTC offset, and selected DST fold, without embedding Resource Booking schema mappings

#### Scenario: Allocation moves to a new resource
- **WHEN** a scheduled activity moves from one Location or Staff member to another
- **THEN** the committed event identifies both the old affected allocation scope and the final replacement scope so the adapter removes old RB occurrences before/while applying the final state idempotently

#### Scenario: Week or duration changes
- **WHEN** a scheduled activity's week set, date semantics, start time, or duration changes
- **THEN** the event's replacement scope permits the adapter to delete occurrences no longer present and create/update the complete final occurrence set

#### Scenario: Variant merge deletes an identity
- **WHEN** applying a schedule or swap merges a variant and deletes an activity identity
- **THEN** the same committed change set contains the surviving final activity state and tombstones for every deleted variant/activity identity

#### Scenario: Activity is unscheduled or deleted
- **WHEN** an allocation is unscheduled or its activity is deleted
- **THEN** the outbox record contains a canonical empty/released final allocation state or tombstone and its previous affected resource/week scope

### Requirement: Every final allocation writer participates
The atomic replacement/outbox rule SHALL apply to normal and bulk Scheduling Engine results, accepted pre-schedule drag/drop results, local manual constraint-break scheduling, booking schedules, unschedule/delete, engine and local swaps, current-resource and scheduled-requirement replacement, week/duration/date changes, variant/JTA merge or deletion, activity/booking imports, simulation/legacy consumers, cascading parent deletes, and scheduling management commands. Direct writers SHALL either call the shared application service, implement the same reviewed transaction boundary, be blocked for scheduled allocations, or be formally decommissioned so no bypass remains.

#### Scenario: Bulk engine response includes successes and no-slot items
- **WHEN** an engine result contains multiple successful assignments and items without `start_slot`
- **THEN** all successful final assignments are applied and represented in one committed change set while no-slot items remain unchanged and are not emitted as successful allocation changes

#### Scenario: Manual constraint-break scheduling bypasses the engine
- **WHEN** `api/views/admin/schedule_request.py::manual_constraint_break()` produces a final schedule
- **THEN** the same atomic mutation, resource-map, replacement-event, and post-commit rules apply as for an engine result

#### Scenario: Booking import writes a scheduled Location
- **WHEN** `api/views/admin/import_table.py::import_tt_booking()` creates an already-scheduled booking and `TtActivityLocation` relation
- **THEN** the booking, week/location relations, resource map, and canonical allocation event commit together

#### Scenario: Current resources are replaced directly
- **WHEN** `resources_update_current`, `resources_update_requirement`, booking swap, engine swap, or an equivalent admin/direct writer replaces Staff or Locations
- **THEN** the replacement state records all old and new affected resources and cannot commit without its outbox record

#### Scenario: Parent deletion cascades scheduled activities
- **WHEN** activity-template, module, or academic-term deletion would cascade-delete scheduled activities
- **THEN** Timetabler captures all prior allocation scope and commits activity tombstones/resource-map changes atomically, or rejects the parent deletion before any scheduled allocation changes

#### Scenario: Week-pattern deletion changes an allocation
- **WHEN** deleting a week pattern would set a scheduled activity's pattern to null or otherwise alter its occurrence set
- **THEN** Timetabler applies an explicit versioned final occurrence replacement atomically or rejects the deletion while the pattern is in use

#### Scenario: Legacy or simulation writer remains routed
- **WHEN** `kafka_consumer/schedule_response.py` or routed `api/views/admin/simulate_schedule_response.py` can mutate authoritative schedules
- **THEN** it uses the shared transaction/outbox service and Phase 2 gate, or is disabled/decommissioned with deployment evidence

### Requirement: Pre-schedule is advisory and read-only
Pre-schedule requests and responses SHALL NOT mutate authoritative allocation state and SHALL NOT emit an allocation change event. Only acceptance of the final schedule application SHALL emit the committed change.

#### Scenario: Pre-schedule candidate is displayed
- **WHEN** the engine returns a pre-schedule preference/candidate response
- **THEN** Timetabler may notify the UI but no activity allocation, resource map, or outbound allocation event changes

#### Scenario: User accepts drag/drop result
- **WHEN** a user turns a pre-schedule candidate into the final drag/drop schedule and the normal engine schedule response commits
- **THEN** exactly one final allocation change set is recorded for the accepted application

### Requirement: Origin and causation metadata are preserved
Every outbound event SHALL include origin, correlation, causation, actor/audit, and request identifiers when available so future acknowledgement/echo handling and end-to-end tracing do not rely on payload comparison alone.

#### Scenario: Engine-originated final schedule
- **WHEN** an engine response is committed
- **THEN** the event includes the engine request ID, response/change-set identity, initiating request correlation, and Timetabler origin

#### Scenario: Administrative direct writer
- **WHEN** an admin endpoint, import, or management command commits a change without an engine request
- **THEN** the event includes a generated correlation/change-set identity and the available actor/command causation metadata

### Requirement: Phase 1 mirrors remain Timetabler-authoritative
During Phase 1, Resource Booking SHALL treat Timetabler-sourced Staff, Locations, activities, and allocations as mirror-only. Resource Booking SHALL reject or disable creation of bookings that would conflict with these mirrored resources until the Phase 2 shared conflict authority is enabled.

#### Scenario: RB user attempts a conflicting Phase 1 booking
- **WHEN** a Resource Booking user attempts to book a resource/time protected by a Timetabler mirror during Phase 1
- **THEN** Resource Booking prevents the write rather than allowing an eventual conflict to be discovered by reconciliation

#### Scenario: Adapter receives an out-of-order version
- **WHEN** the adapter receives a version older than the version already applied to a mirror
- **THEN** it ignores/quarantines the stale delivery and does not regress the Resource Booking mirror
