docs: record labels migration verification

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
Thorsten Bus 2026-05-03 22:10:46 +02:00
parent 767e22eac8
commit 860db0405f
4 changed files with 122 additions and 0 deletions

View file

@ -0,0 +1,9 @@
php artisan test tests/Feature/Migrations/LabelsTableTest.php
PASS Tests\Feature\Migrations\LabelsTableTest
✓ labels table has expected columns 0.40s
✓ labels table enforces unique name 0.01s
✓ labels table allows nullable color 0.01s
Tests: 3 passed (4 assertions)
Duration: 0.54s

View file

@ -0,0 +1,33 @@
migrate:fresh output
Dropping all tables ........................................... 13.01ms DONE
INFO Preparing database.
Creating migration table ....................................... 4.76ms DONE
INFO Running migrations.
0001_01_01_000000_create_users_table ........................... 9.49ms DONE
0001_01_01_000001_create_cache_table ........................... 4.35ms DONE
0001_01_01_000002_create_jobs_table ............................ 4.88ms DONE
2026_03_01_100000_extend_users_table ........................... 4.60ms DONE
2026_03_01_100100_create_services_table ........................ 2.92ms DONE
2026_03_01_100200_create_songs_table ........................... 2.08ms DONE
2026_03_01_100300_create_song_groups_table ..................... 3.09ms DONE
2026_03_01_100400_create_song_slides_table ..................... 5.10ms DONE
2026_03_01_100500_create_song_arrangements_table ............... 3.19ms DONE
2026_03_01_100600_create_song_arrangement_groups_table ......... 3.61ms DONE
2026_03_01_100700_create_service_songs_table ................... 3.25ms DONE
2026_03_01_100800_create_slides_table .......................... 3.68ms DONE
2026_03_01_100900_create_cts_sync_log_table .................... 4.32ms DONE
2026_03_02_100000_create_api_request_logs_table ................ 2.15ms DONE
2026_03_02_121522_add_response_body_to_api_request_logs_table .. 1.31ms DONE
2026_03_02_130249_add_cts_song_id_to_songs_and_service_songs_tables 3.30ms DONE
2026_03_02_140000_add_sort_order_to_slides_table ............... 0.91ms DONE
2026_03_02_200000_create_settings_table ........................ 2.48ms DONE
2026_03_29_100001_create_service_agenda_items_table ............ 3.03ms DONE
2026_03_29_100002_add_service_agenda_item_id_to_slides_table .. 13.03ms DONE
2026_03_29_131045_add_missing_cts_song_id_to_service_songs_table 0.53ms DONE
2026_03_29_131359_add_has_agenda_to_services_table ............. 1.24ms DONE
2026_05_03_100100_create_labels_table .......................... 2.50ms DONE

View file

@ -0,0 +1,37 @@
# Decisions — macros-and-labels-import
## [2026-05-03] Architectural Decisions
### Schema
- **labels table**: global, unique by name, nullable color, hidden_at (NOT deleted_at)
- **macros table**: unique by uuid (uppercase), hidden_at (NOT deleted_at)
- **macro_assignments**: restrictOnDelete on macro_id and label_id FKs
- **service_macro_overrides**: existence of row = override active; no extra boolean
- **song_arrangement_labels**: replaces song_arrangement_groups; references global label_id
### Macro Assignment Semantics
- `part_type` enum: `information | moderation | sermon | song | agenda_item`
- `position` enum: `all_slides | first_slide | last_slide | by_label`
- `by_label` is valid for ALL part_types (not songs-only) — validated at app level if restriction needed
- Stacking: multiple assignments can fire on same slide — all applied in `order ASC`
- Override wins 100% — no globals bleed through when override exists
### Override Semantics
- "Anpassen" snapshots current globals into `service_macro_assignments` rows
- "Auf Standard zurücksetzen" deletes the override row + cascades service_macro_assignments
- German tooltip: "Erstellt eine Kopie der aktuellen globalen Zuweisungen für diesen Gottesdienst. Spätere Änderungen an den globalen Zuweisungen wirken sich auf diesen Gottesdienst NICHT mehr aus."
### Data Migration
- Destructive: `up()` deletes songs, song_groups, song_slides, song_arrangements, song_arrangement_groups
- `down()` throws RuntimeException (irreversible)
- Guard: `if (!Schema::hasTable('song_groups') || !DB::table('song_groups')->exists()) return;`
- Old 4 macro settings keys → migrated to global assignment if all present; then deleted
### Label Color Priority
1. Labels file import → always sets/overwrites color
2. .pro song import → only sets color on CREATE (new label); existing color preserved
3. UI → read-only (no manual edit)
### Current Migration Scope
- `labels` migration only defines the schema; no model or business logic belongs in this task
- Use `hidden_at` instead of `deleted_at` to align with soft-hide semantics

View file

@ -0,0 +1,43 @@
# Learnings — macros-and-labels-import
## [2026-05-03] Session ses_210cd1557ffeGs4SEGrt7hnvyS — Plan Created
### Parser Library
- Source at `/Users/thorsten/AI/propresenter/src/` (NOT `/Users/thorsten/AI/propresenter-work/php/` per stale AGENTS.md)
- VCS repo: `https://git.stadtmission-butzbach.de/public/propresenter-php.git` (dev-master)
- New classes (NOT yet in vendor/): `MacrosFileReader`, `LabelsFileReader`, `Macro`, `MacroLibrary`, `MacroCollection`, `Label`, `LabelLibrary`
- `MacrosFileReader::read(string $filePath): MacroLibrary` — raw protobuf binary, no extension
- `LabelsFileReader::read(string $filePath): LabelLibrary` — same
- `Label::getName()` returns protobuf `text` field — name is the identity (no UUID for labels)
- `Macro::getColor()` returns `?array{r,g,b,a}` floats 0..1 — need `MacroColorConverter` to get hex
- `Label::getColorHex()` already returns `#RRGGBB` — mirror its formula for macros
- **PHP 8.4 required** by parser. App currently requires `^8.2` — BLOCKER for T0.1
### DB Schema Key Facts
- `slides.type` enum is `[information, moderation, sermon]` ONLY — no `agenda_item`
- `agenda_item` part_type = slide where `service_agenda_item_id IS NOT NULL` at runtime
- `song_groups.color` is NOT NULLABLE (migration says so) — new `labels.color` IS nullable
- `service_songs.song_id` is `cascadeOnDelete` — wiping `songs` auto-cascades to `service_songs`
### Export Flow
- `ProExportService::buildGroups()` lines 38-69 — macro injection point
- `ProExportService::buildMacroData()` lines 71-86 — reads 4 legacy settings keys
- Currently injects macro ONLY when group name is "COPYRIGHT" (case-insensitive)
- `ProImportService::import(UploadedFile $file): array` — method signature (NOT `importFromFile`)
### Settings Pattern
- `Setting::get($key, $default)` / `Setting::set($key, $value)` — simple key/value
- `settings` table: `key UNIQUE, value TEXT`
### Critical Decisions
- song_groups → labels: global table, "drop all data" migration (no backwards compat)
- Hybrid macro scope: global defaults in Settings; per-(service, part_type) override via "Anpassen"
- Override = snapshot of globals at creation time; future global changes don't propagate
- Stacking: all matching assignments fire, ordered by `macro_assignments.order ASC`
- Hidden macros/labels: skip at export, warning badge in editor
- Label colors: read-only in UI; Labels file import is sole authority; .pro auto-discovery only sets color on CREATE
- FK rules: `restrictOnDelete` on macro/label refs (use `hidden_at`); `cascadeOnDelete` on service-scoped rows
### Migration/Test Notes
- `tests/Pest.php` already applies `RefreshDatabase` to all `Feature` tests; no extra setup needed for `Feature/Migrations`
- SQLite unique constraint errors can be asserted with `->toThrow(\Exception::class)` in migration tests