feat(queue): archiveren i.p.v. verwijderen — ops-db, purge naar cold-store, UI-archiefweergave (M32) #82

Merged
janpeter merged 2 commits from feat/m32-archivering into main 2026-08-12 20:32:58 +02:00
Owner

M32 slice 1, Tasks 10–12 van docs/plans/M32-queue-archivering.md (DUBBEL-GO r7, a476639). Spec §4.5 + besluit B14 (6d8ea8c).

Kern: via de gewone UI verdwijnt er niets meer

  • deleteAgentMessage en deleteMessageAction bestaan niet meer. Ervoor in de plaats: archiveAgentMessage / unarchiveAgentMessage — bericht + volledige transitieve reply-subtree, in één transactie met FOR UPDATE, alleen terminale rijen (QUEUE_NOT_TERMINAL-equivalent: leesbare fout mét het blokkerende id), per rij idempotent, géén pg_notify. Twee tests bewaken dat de oude exports weg blijven.
  • De retention-purge is archive-then-delete: archiveClosedMessageThreadsOlderThan (hernoemd) schuift de thread eerst naar agent_message_archive en verwijdert hem dan, in één statement. MessageRetentionPurge krijgt archivedRows; CLOSED_THREAD_TARGETS_CTE bleef ongewijzigd (al recursief) zodat dry-run en purge niet kunnen divergeren.
  • WORKERS_ARCHIVED_COLUMNS is de enige bron voor beide clausules van de INSERT — spiegel van s4m-queue's ARCHIVED_COLUMNS. Een kolom die hier ontbreekt maar wél in beide tabellen staat, zou stil niet gearchiveerd worden.
  • clearAgentMessageQueue blijft destructief — bewust JP-besluit (B14), inclusief confirm-dialog en dry-run-tellingen.

UI

Weergave-toggle Actief / Archief (role="tablist"), rij-actie archiveer (alleen op terminale rijen — de server zou de rest weigeren) respectievelijk herstel, beide zonder bevestiging omdat ze omkeerbaar zijn. De purge-dialoog zegt nu wat er echt gebeurt: threads verhuizen naar de cold-store, data blijft bewaard. docs/pages/queue-messages.md is op alle drie de plekken bijgewerkt (prosa, componentbeschrijving, actie-inventaris).

Gate

npm run verify && npm run buildgroen; 121 bestanden, 868 tests.

De realdb-suite is niet overgeslagen: gedraaid tegen een echte Postgres (wegwerp-container, OPS_TEST_DATABASE_URL) → 10/10 groen, inclusief de nieuwe cases: een diepte-2-keten verhuist integraal naar agent_message_archive (ook het kleinkind), archiveren weigert fail-closed zolang de keten een pending rij bevat (nul rijen half gemarkeerd), en archive/unarchive zijn per rij idempotent ({total: 3, archived: 0} bij de tweede aanroep).

De fixture __tests__/lib/queue/fixtures/agent_message.sql was een verouderde Ops-kopie (geen idempotency_key, geen archieftabel). Ververst naar de actuele productievouw uit de drie Scrum4Me-migraties, inclusief M32; beforeEach bouwt nu beide tabellen op.

Volgorde

Deze code vereist de kolom uit PR Scrum4Me#154 (migratie) — merge/deploy pas ná die migratie, plan-Task 13.

🤖 Generated with Claude Code

M32 slice 1, Tasks 10–12 van `docs/plans/M32-queue-archivering.md` (DUBBEL-GO r7, `a476639`). Spec §4.5 + besluit B14 (`6d8ea8c`). ## Kern: via de gewone UI verdwijnt er niets meer - `deleteAgentMessage` en `deleteMessageAction` **bestaan niet meer**. Ervoor in de plaats: `archiveAgentMessage` / `unarchiveAgentMessage` — bericht + volledige transitieve reply-subtree, in één transactie met `FOR UPDATE`, alleen terminale rijen (`QUEUE_NOT_TERMINAL`-equivalent: leesbare fout mét het blokkerende id), per rij idempotent, **géén pg_notify**. Twee tests bewaken dat de oude exports weg blijven. - De retention-purge is **archive-then-delete**: `archiveClosedMessageThreadsOlderThan` (hernoemd) schuift de thread eerst naar `agent_message_archive` en verwijdert hem dan, in één statement. `MessageRetentionPurge` krijgt `archivedRows`; `CLOSED_THREAD_TARGETS_CTE` bleef ongewijzigd (al recursief) zodat dry-run en purge niet kunnen divergeren. - `WORKERS_ARCHIVED_COLUMNS` is de enige bron voor beide clausules van de INSERT — spiegel van s4m-queue's `ARCHIVED_COLUMNS`. Een kolom die hier ontbreekt maar wél in beide tabellen staat, zou stil niet gearchiveerd worden. - **`clearAgentMessageQueue` blijft destructief** — bewust JP-besluit (B14), inclusief confirm-dialog en dry-run-tellingen. ## UI Weergave-toggle *Actief* / *Archief* (`role="tablist"`), rij-actie **archiveer** (alleen op terminale rijen — de server zou de rest weigeren) respectievelijk **herstel**, beide zonder bevestiging omdat ze omkeerbaar zijn. De purge-dialoog zegt nu wat er echt gebeurt: threads verhuizen naar de cold-store, data blijft bewaard. `docs/pages/queue-messages.md` is op alle drie de plekken bijgewerkt (prosa, componentbeschrijving, actie-inventaris). ## Gate `npm run verify && npm run build` → **groen**; 121 bestanden, **868 tests**. De realdb-suite is **niet overgeslagen**: gedraaid tegen een echte Postgres (wegwerp-container, `OPS_TEST_DATABASE_URL`) → **10/10 groen**, inclusief de nieuwe cases: een diepte-2-keten verhuist integraal naar `agent_message_archive` (ook het kleinkind), archiveren weigert fail-closed zolang de keten een `pending` rij bevat (nul rijen half gemarkeerd), en archive/unarchive zijn per rij idempotent (`{total: 3, archived: 0}` bij de tweede aanroep). De fixture `__tests__/lib/queue/fixtures/agent_message.sql` was een verouderde Ops-kopie (geen `idempotency_key`, geen archieftabel). Ververst naar de actuele productievouw uit de drie Scrum4Me-migraties, inclusief M32; `beforeEach` bouwt nu beide tabellen op. ## Volgorde Deze code vereist de kolom uit PR Scrum4Me#154 (migratie) — merge/deploy pas ná die migratie, plan-Task 13. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
s4m-codex-reviewer left a comment

Verdict: COMMENT

Geen gekoppeld plan gevonden — beoordeeld op codekwaliteit + product-standaarden.

Findings

  • minor — __tests__/app/queue-messages-view.test.tsx:101 — De test "archiveren wordt alleen aangeboden op een terminale rij" rendert eerst een terminale rij en daarna opnieuw een pending-rij zonder cleanup/rerender-isolatie. Daardoor blijft de eerste archiveer-knop in de DOM staan en bewijst toHaveLength(1) niet dat de pending-rij géén archiveerknop krijgt. Dit maskeert precies de regressie die de test wil afdekken. Maak de renders geïsoleerd of assert na de tweede render expliciet op de pending-rij/container.

Reviewnotities

De backend-wijzigingen volgen de bestaande queue-patronen: admin-guards blijven in de server-actions, listAgentMessages default naar actieve berichten, purge gebruikt archive-then-delete in één statement, en archive/unarchive locken de subtree transactioneel. De docs voor /queue/messages zijn mee aangepast. Geen blokkerende findings gevonden.

# Verdict: COMMENT Geen gekoppeld plan gevonden — beoordeeld op codekwaliteit + product-standaarden. ## Findings - minor — `__tests__/app/queue-messages-view.test.tsx:101` — De test "archiveren wordt alleen aangeboden op een terminale rij" rendert eerst een terminale rij en daarna opnieuw een pending-rij zonder cleanup/rerender-isolatie. Daardoor blijft de eerste `archiveer`-knop in de DOM staan en bewijst `toHaveLength(1)` niet dat de pending-rij géén archiveerknop krijgt. Dit maskeert precies de regressie die de test wil afdekken. Maak de renders geïsoleerd of assert na de tweede render expliciet op de pending-rij/container. ## Reviewnotities De backend-wijzigingen volgen de bestaande queue-patronen: admin-guards blijven in de server-actions, `listAgentMessages` default naar actieve berichten, purge gebruikt archive-then-delete in één statement, en archive/unarchive locken de subtree transactioneel. De docs voor `/queue/messages` zijn mee aangepast. Geen blokkerende findings gevonden.
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
janpeter/scrum4me-workers!82
No description provided.