Skip to content

feat(audit): Sprint 16 §5.1 — deletedBy actor on every soft-deletable model#15

Open
dobrodob wants to merge 1 commit intofeat/sprint-16-b-observabilityfrom
feat/sprint-16-c-deleted-by-audit
Open

feat(audit): Sprint 16 §5.1 — deletedBy actor on every soft-deletable model#15
dobrodob wants to merge 1 commit intofeat/sprint-16-b-observabilityfrom
feat/sprint-16-c-deleted-by-audit

Conversation

@dobrodob
Copy link
Copy Markdown
Member

Summary

Sprint 16 §5.1 — extends the existing soft-delete foundation (pattern 18) with an audit column: who removed the row, not just when. Answers the "who nuked this?" question without spinning up a parallel audit-log table.

PR 3 of 7 for Sprint 16 — stacked on top of #14.

Schema changes

Migration `20260419163543_sprint_16_deleted_by_audit`:

  • Add `deleted_by INT NULL` + FK(SET NULL) to: authors, communities, conversations, files, messages, shout_reactions_followers, bookmark_lists.
  • Promote the pre-existing `deleted_by` int on drafts to a typed FK with `ON DELETE SET NULL`. (Shouts and Reactions already had proper FKs from earlier sprints.)
  • Corresponding Prisma relations + 7 reverse relations on `Author`.

Hand-edited migration SQL to strip Prisma-generated `DROP DEFAULT` statements on tsvector columns — Postgres rejects those on generated columns (`column is a generated column`). Rationale documented in the migration file header.

Service wiring

Service Before After
`shout.softDelete` Already wrote `deletedBy` No change
`draft.softDelete` Already wrote `deletedBy` No change
`reaction.softDelete` Already wrote `deletedBy` No change
`bookmark-list.softDelete` `deletedAt` only + `deletedBy: authorId`
`follower.unfollowShout` `deletedAt` only + `deletedBy: followerId`
`file.softDelete` `deletedAt` only + `deletedBy: userId`
`message.softDelete` `deletedAt` only + `deletedBy: userId`
`community.softDelete(id)` no actor `community.softDelete(id, actorId: number|null = null)` — null = system teardown

Test plan

  • New `apps/core/test/deleted-by-audit.integration.spec.ts` — 6 regression-guard tests, one per call-site. Soft-delete a row, read back, assert `deletedBy === actor.id`.
  • Existing 86 integration tests (bookmarks / inbox / file / community / moderation / profile) still green.
  • `npx prisma migrate deploy` succeeds against dev DB.

Migration notes

  • Migration is additive (no drops). Safe on a populated prod DB.
  • Rows with pre-existing `deletedAt` values keep `deletedBy = NULL` (unknown) — backfill via a separate job if the audit trail matters for old rows.
  • `ON DELETE SET NULL` on the FK means: deleting the actor Author (GDPR hard delete) doesn't break the audit trail; the pointer just goes null.

Docs

  • New pattern: `docs/discours-patterns/43-deleted-by-actor-audit.md` — why audit trail lives in-row (not a parallel table), GDPR-friendly SET NULL semantics, adaptation notes.

🤖 Generated with Claude Code

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 19, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 43582a0b-3303-4886-9b0c-90a7f3e39767

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/sprint-16-c-deleted-by-audit

Comment @coderabbitai help to get the list of available commands and usage tips.

@dobrodob
Copy link
Copy Markdown
Member Author

@coderabbitai review — this PR was marked as skipped earlier. Please review explicitly.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 19, 2026

@dobrodob: Sure! I'll kick off a full review of PR #15 now.


🧠 Learnings used
Learnt from: CR
Repo: Discours/publy-backend PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-19T12:24:18.182Z
Learning: Applies to apps/core/src/**/*.service.ts : Always check `deletedAt: null` in database queries to respect soft delete pattern on all entities.
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@dobrodob dobrodob force-pushed the feat/sprint-16-c-deleted-by-audit branch from 2f2a39d to eb2ae06 Compare April 19, 2026 23:18
@dobrodob dobrodob force-pushed the feat/sprint-16-b-observability branch from 8e3c542 to dddf31f Compare April 19, 2026 23:57
@dobrodob dobrodob force-pushed the feat/sprint-16-c-deleted-by-audit branch from eb2ae06 to 1eac3c4 Compare April 19, 2026 23:58
… model

Extends the existing soft-delete foundation (pattern 18) with an audit
column: WHO removed the row, not just WHEN. Answers the "who nuked this?"
question without spinning up a parallel audit-log table.

Schema changes (migration 20260419163543):
- Add `deleted_by INT NULL` + FK(SET NULL) to authors, communities,
  conversations, files, messages, shout_reactions_followers, bookmark_lists.
- Promote the pre-existing `deleted_by` int on `drafts` to a typed FK with
  ON DELETE SET NULL. (Shouts and Reactions already had proper FKs.)
- Corresponding Prisma relations + reverse relations on Author (7 new).
- Hand-edited migration SQL to strip Prisma-generated `DROP DEFAULT` on
  tsvector columns — Postgres rejects those on generated columns. Rationale
  documented in the migration file header.

Service wiring:
- community.softDelete now takes `actorId: number | null` (explicit typing
  for system-triggered teardowns — seed scripts, migration cleanup).
- bookmark-list / follower / file / message services pass the actor ID on
  every soft-delete write.
- shout / draft / reaction already did (from earlier sprints) — left as-is.

Regression guard:
- deleted-by-audit.integration.spec.ts — 6 tests, one per service path:
  soft-delete a row, read it back, assert `deletedBy === actor.id`. Protects
  against a future refactor silently dropping the actor.

Pattern doc:
- 43-deleted-by-actor-audit.md — why audit trail lives in-row (not a
  parallel table), GDPR-friendly SET NULL semantics, adaptation notes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dobrodob dobrodob force-pushed the feat/sprint-16-b-observability branch from dddf31f to 1e7a5c1 Compare April 20, 2026 00:53
@dobrodob dobrodob force-pushed the feat/sprint-16-c-deleted-by-audit branch from 1eac3c4 to f9c5db1 Compare April 20, 2026 00:53
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