Skip to content

docs(soft-delete): projekt wdrożeniowy + plany TDD (publikacje + autorzy)#312

Draft
mpasternak wants to merge 9 commits into
devfrom
feat/soft-delete
Draft

docs(soft-delete): projekt wdrożeniowy + plany TDD (publikacje + autorzy)#312
mpasternak wants to merge 9 commits into
devfrom
feat/soft-delete

Conversation

@mpasternak

Copy link
Copy Markdown
Member

Co to jest

Pełny projekt wdrożeniowy (spec) + 9 planów implementacyjnych TDD dla soft-delete. Same dokumenty (docs/superpowers/), zero kodu produkcyjnego. Zastępuje feasibility-spec z #304 (tamten obejmował tylko publikacje i był „ODŁOŻONY").

Refs #303.

Zakres

  • Publikacje (5 modeli) → SoftDeleteModel + wąska kaskada na *_Autor (powód: 90 miejsc *_Autor.objects w ewaluacji staje się poprawnych z domyślnego menedżera).
  • Autor → soft-delete tylko bez prac; autor z pracami = PROTECT (ten sam wzorzec dla książki-matki z rozdziałami).
  • Spójność cache = filtr deleted_at IS NULL w widokach źródłowych (funkcji triggera bpp_refresh_cache NIE ruszamy — jest osobno optymalizowana).
  • PBN → wycofanie oświadczeń instytucji przez rozszerzoną pbn_export_queue (operacja WYCOFANIE); restore → WYSYLKA.
  • AudytSoftDeleteLog (kto/kiedy/dlaczego + status PBN), zasilany sygnałami pakietu.
  • Admin superuser-only (kosz / filtr / przywróć / usuń-trwale / powód).

Status — DRAFT, implementacja wstrzymana

  • ✅ Dokumentacja kompletna: spec + indeks (00) + 8 planów fazowych (01–08), po self-review i korektach spec↔kod.
  • Faza 01 = BLOKER: zależy od równoległej optymalizacji funkcji bpp_refresh_cache() (gałąź perf/cache-trigger-pk-filter). Po jej wylądowaniu: rebase feat/soft-delete, weryfikacja inwariantu „bezwarunkowy DELETE przed re-insertem", potem realizacja fazami.

Pliki

  • docs/superpowers/specs/2026-06-04-soft-delete-publikacje-i-autorzy-design.md
  • docs/superpowers/plans/2026-06-04-soft-delete-0{0..8}-*.md

🤖 Generated with Claude Code

mpasternak and others added 9 commits June 4, 2026 11:00
…ji (ODŁOŻONE)

Analiza wprowadzenia soft-delete dla Wydawnictwo_Ciagle/Zwarte,
Praca_Doktorska, Praca_Habilitacyjna, Patent. Status: świadomie
odłożone — to spec/rozpoznanie, nie zlecenie implementacji.

Kluczowe ustalenia:
- choke-point w triggerze bpp_refresh_cache(): "deleted_at IS NOT NULL"
  traktowany jak DELETE → wszystko czytające przez Rekord/Cache_* czyści
  się jednym ruchem,
- django-soft-delete już w repo (pyproject.toml), precedens w
  zglos_publikacje; domyślny manager ukrywa usunięte → kat. A czysta
  za darmo, kat. B (import/dedup/PBN) musi przejść na global_objects,
- kaskada/auto-undelete pakietu zweryfikowana w kodzie: strict=True
  wymaga by dzieci były SoftDeleteModel → rekomendacja Projekt A
  (override delete(), dzieci nietknięte, cache/trigger robi resztę),
- slug unique → warunkowy UniqueConstraint(deleted_at__isnull=True),
- szacunek ~2-3 tygodnie; otwarte decyzje spisane.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Zatwierdzony design (brainstorming 2026-06-04) rozszerzający feasibility-spec
o: soft-delete autora (PROTECT z pracami / soft-delete husku bez prac),
wycofanie oświadczeń z PBN przez rozszerzenie pbn_export_queue (operacja
WYCOFANIE, async+retry), dedykowany SoftDeleteLog zasilany sygnałami pakietu,
oraz admin superuser-only (kosz/filtr/przywróć/usuń-trwale).

Kluczowe decyzje:
- asymetria: pełny SoftDeleteModel dla 5 publikacji (Projekt A, trigger jako
  choke-point), ale autor soft-delete TYLKO bez prac → through-modele/doktorat
  /habilitacja NIE stają się soft-delete (mały blast radius),
- flip FK autor CASCADE→PROTECT + guard w soft delete() (PROTECT nie łapie
  UPDATE-owego soft-delete),
- synergia z deduplikator_autorow: husk po merge staje się odwracalny,
- PBN: wycofanie oświadczeń instytucji (delete_all_publication_statements),
  obiektu publikacji nie kasujemy; restore → re-WYSYLKA,
- retencja: brak auto-czyszczenia, tylko ręczny hard-delete.

Stary 2026-06-03-soft-delete-publikacje.md oznaczony jako ZASTĄPIONY.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… trigger

Aktualizacja designu po decyzji użytkownika:
- 3 through-modele (Wydawnictwo_Ciagle/Zwarte_Autor, Patent_Autor) stają się
  SoftDeleteModel jako cel WĄSKIEJ kaskady z soft-delete publikacji (wspólny
  transaction_id), nie pełnego refleksyjnego Projektu B — pozostałe dzieci
  (*_Streszczenie itd.) nietknięte, kaskada niewirusowa,
- powód: 90 bezpośrednich zapytań *_Autor.objects (głównie ewaluacja_
  optymalizacja) staje się poprawnych z domyślnego menedżera — eliminuje
  ryzyko silent-leak skasowanej pracy do ewaluacji,
- trigger UPROSZCZONY: wszystkie 8 tabel pod triggerem mają własne deleted_at
  → reguła "deleted_at IS NOT NULL → DELETE" jednolita, BEZ JOIN do rodzica;
  widoki źródłowe filtrują po własnej kolumnie,
- guard autora MUSI liczyć przez global_objects (kaskadowo-skasowane
  autorstwa są ukryte przed default objects) — inaczej autor z pracami
  tylko-w-koszu przeszedłby przez guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Self-review wykrył 3 luki materialne + drobiazgi:
- A2: filtr deleted_at w widokach źródłowych ustawiony jako mechanizm #1
  (pokrywa odczyt z bpp_rekord, verify_cache, re-insert); trigger-skip
  zdegradowany do opcjonalnej optymalizacji — sam nie wystarcza,
- A3: doprecyzowana semantyka SentData przy WYCOFANIE (submitted_successfully
  =False + znacznik, bez kasowania wiersza),
- A1/§2.6: dodana pominięta self-referencja Wydawnictwo_Zwarte (rozdziały →
  książka-matka) z proponowanym defaultem (brak kaskady + ostrzeżenie) do
  potwierdzenia; + nota o GenericForeignKey (soft-delete bezpieczniejszy),
- B1: doprecyzowany Cel (powiązania *_Autor soft-deletowane, nie "aktywne"),
- B2: kolejność migracji w fazie 1 (deleted_at column przed trigger/widok),
- §10: dopisane decyzje #9/#10 + sekcja "oczekuje potwierdzenia".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…autora)

Decyzja A1: soft-delete książki-matki zablokowany, jeśli ma rozdziały.
Dwuwarstwowy wzorzec identyczny z guardem autora: flip FK wydawnictwo_nadrzedne
CASCADE→PROTECT + guard w soft delete() liczący rozdziały przez global_objects.
Eliminuje problem "rozdziały wskazujące na skasowaną książkę" u źródła.
Zaktualizowane §2.6, §8 (faza 4 = guardy PROTECT), §10 (decyzja #11).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Plan-indeks (00) z wpiętymi wspólnymi kontraktami + 8 planów fazowych w
formacie superpowers:writing-plans (bite-sized TDD, bez placeholderów):
01 *_Autor SoftDeleteModel + widoki/trigger + spójność cache
02 publikacje SoftDeleteModel + wąska kaskada na *_Autor + slug + menedżery
03 audyt kategorii B (global_objects, hard_delete w pbn_import)
04 guardy PROTECT (autor + książka-matka) + flip FK
05 PBN wycofanie przez pbn_export_queue (operacja WYCOFANIE) + restore WYSYLKA
06 SoftDeleteLog + receivery sygnałów + atrybucja usera (thread-local)
07 admin superuser-only (kosz/filtr/przywróć/usuń-trwale/powód)
08 testy regresji E2E

Plany rozpisane przez równoległych agentów z wglądem w realny kod.
Wykryte rozbieżności spec↔kod do naniesienia osobnym commitem.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Weryfikacja w realnym kodzie (przez agentów rozpisujących fazy) ujawniła:
- C1: aktualny trigger to 0399_fix_refresh_cache_upsert.sql (upsert ON CONFLICT
  + advisory locks, DELETE-przed-upsertem bezwarunkowy), nie baseline 0001;
  + nota o utajonym bugu (string in lista-krotek) do poprawy w fazie 01,
- C2: verify_cache.py to martwy stub (NotImplementedError + hardcoded host) —
  spójność weryfikujemy przez Rekord.objects.full_refresh(), nie verify_cache,
- C3: slug to pole @denormalized (django-denorm-iplweb), nie proste unique=True
  — zdjąć unique z kwargs denorm + UniqueConstraint w Meced konkretnych klas,
- C5: PBN_Export_Queue.zamowil jest NOT NULL → konto techniczne dla
  zakolejkowań systemowych (nie nullable),
- reconcyliacja API thread-local: kanoniczne soft_delete_context(user,reason)
  + get_soft_delete_user/reason (faza 06 tworzy, 07 używa).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Funkcja bpp_refresh_cache optymalizowana w osobnej gałęzi (prace użytkownika).
Faza 01 = BLOKER do czasu wskazania tej gałęzi i aktualizacji feat/soft-delete.
Ortogonalność: faza 01 dotyka widoków źródłowych (filtr deleted_at), optymalizacja
dotyka funkcji triggera. Zapisany inwariant do weryfikacji: bezwarunkowy DELETE
przed re-insertem/upsertem (na nim wisi wystarczalność filtra widoku).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Decyzja użytkownika: bpp_refresh_cache() optymalizowany osobno; faza 01 zmienia
WYŁĄCZNIE widoki źródłowe (filtr deleted_at IS NULL). Trigger-skip i fix
utajonego buga z krotkami WYCIĘTE z fazy 01. Box AKTUALIZACJA ZAKRESU na górze
planu 01 + jednoznaczna nota w indeksie. Inwariant do weryfikacji po wskazaniu
gałęzi optymalizacji: bezwarunkowy DELETE przed re-insertem/upsertem.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant