fix(data): protect portfolio publication invariant

This commit is contained in:
2026-06-01 09:20:27 +03:00
parent 5809a470b9
commit d591e5ed5a
4 changed files with 114 additions and 5 deletions
@@ -79,6 +79,8 @@ CHECK (NOT is_public OR (
Application validation additionally requires at least one linked completed session and at least one linked GM before publishing because those requirements span child tables.
Database triggers on `portfolio_game_sessions` and `portfolio_game_masters` protect the same invariant after link removal. After a link is deleted, the trigger locks the parent card, checks both required link sets, and sets `is_public = false` with a fresh `updated_at` when either set is empty. It deliberately preserves `published_at` as the timestamp of the first publication. The link foreign keys retain `ON DELETE CASCADE`; when the card itself or its owning club is deleted, the trigger update is a harmless no-op for the disappearing parent row.
### `portfolio_game_sessions`
| Column | Type | Constraints | Description |
@@ -125,7 +127,7 @@ UNIQUE (portfolio_game_id, author_player_id)
```
- Partial public index on `(portfolio_game_id, created_at DESC)` where `moderation_status = 'Approved'` and `publication_consent_at IS NOT NULL`.
- Partial moderation index on `(created_at)` where `moderation_status = 'Pending'`.
- Partial moderation index on `(portfolio_game_id, created_at DESC)` where `moderation_status = 'Pending'`.
---
@@ -339,7 +341,7 @@ Follow TDD for production changes.
### Schema And Contracts
- Migration source-contract tests assert the four new tables, constraints, and indexes.
- Migration source-contract tests assert the four new tables, format constraint, publication guard, case-insensitive slug uniqueness, group and GM-profile indexes, card-oriented pending-review index, and the link-removal trigger protection.
- Public DTO reflection/source tests assert that private identifiers and physical storage paths are absent.
- Existing showcase tests continue to assert the future-session catalog boundary.