fix(appview): separate ban enforcement from user-initiated deletes (ATB-25) (#56)
* fix(indexer): strip title from reply records at index time (ATB-35)
When a space.atbb.post record carries both a reply ref and a title field,
the indexer now coerces title to null for both INSERT and UPDATE paths.
This enforces the lexicon invariant ("title is omitted for replies") at
the data-ingestion boundary rather than relying on the API or UI to ignore
the field.
ATB-36 is a duplicate of this issue and has been marked accordingly.
* fix(indexer): address PR review feedback (ATB-35)
- Add try-catch to getPostIdByUri, matching the structured error logging
pattern used by all other helper methods
- Warn when reply ref is present but missing $type (rootPostId/parentPostId
will be null; operators now have an observable signal)
- Reaction stub handlers: info → warn (events are being permanently
discarded, not successfully processed)
- Tests: extract createTrackingDb helper to eliminate inline mock duplication
- Tests: add topic-starter title-preserved assertions (right branch of ternary)
- Tests: assert full inserted shape on reply create (rootPostId/parentPostId
null, rootUri/parentUri populated) to document the $type-less behavior
* fix(appview): separate ban enforcement from user-initiated deletes (ATB-25)
- Add bannedByMod column to posts: applyBan/liftBan now use this column
exclusively, so lifting a ban can never resurrect user-deleted content
- Add deletedByUser column and tombstone user-initiated deletes: when a
post delete arrives from the firehose, the row is kept (for FK stability)
but text is replaced with '[user deleted this post]' and deletedByUser
is set to true — personal content is gone, thread structure is preserved
- Remove shared deleted column; all API filters now use bannedByMod=false
- Migrations: 0009 adds banned_by_mod, 0010 drops deleted / adds deleted_by_user
* test: fix schema column list test and strengthen tombstone assertion
- schema.test.ts: update "has expected columns for the unified post model"
to check for bannedByMod/deletedByUser instead of deleted (was missed in
the original commit, causing CI to fail)
- indexer.test.ts: replace weak toHaveBeenCalled() guard with exact
payload assertion per code review — verifies text and deletedByUser are
set, and that bannedByMod/deleted are never touched
* docs: correct genericDelete comment — posts always tombstone, no fallback