feat: moderation UI wrappers (Stage 3) + slice-1 audit gap tests #54

Merged
starsetbyte merged 12 commits from feat/moderation-ui-wrappers into main 2026-07-05 12:42:47 +00:00
Owner

Stage 3 of the moderation foundation (plan: docs/superpowers/plans/2026-07-05-moderation-ui-wrappers.md, in-tree on this branch's session — roadmap on docs/moderation-foundation). Stacked on #53 — retarget to main after #53 merges.

Zero user-visible behavior change. 11 commits, 13 new files, no existing file modified. Nothing consumes these components yet; Stage 4 wires them into timeline/thread/profile/quotes/search/notifications.

What's here

  • ui/common/moderation/: ContentCover, MediaCover, HiddenPostRow, LabelBadge, LabelsBottomSheet, PostOverflowMenu, pure ModerationCopy (no raw-DID leaks, tested), ModerationPreviews
  • Contract tests: cover files forbid coil3/.clip(/graphicsLayer/.blur(; media/content composed only behind the reveal gate; sheet hoisting locked out of PostCard
  • Slice-1 audit gap closure: ModerationResolverNotificationTest (6 tests), SchemaV10ContractTest (locks the v10 schema delta + migration SQL)

Verification

  • ./gradlew test: 329 unit tests, 0 failures (23 new)
  • assembleDebug: success
  • lint: only the 2 pre-existing NewApi errors in AtProtoOAuthClient.kt (untouched here, predate this branch)
  • Final whole-branch review (opus): Ready to merge — 0 Critical, 0 Important; recommended fixes applied (Role.Button on LabelBadge, trailing newline)

Notes for Stage 4 wiring (do not lose)

  • LabelBadge/LabelsBottomSheet take List<LabelCause> (unstable type): callers must pass a remember(post.uri)-ed list or the header recomposes every pass
  • Wrapper modifier applies to the cover branch only; apply layout modifiers to the inner content, not the wrapper
  • HiddenPostRow does no internal noOverride check — callers gate eligibility
  • LabelsBottomSheet uses default sheet state (house RepostBottomSheet passes one explicitly) — reconcile when wiring
  • Previews compile but need a human glance in Android Studio (headless session)
  • SchemaV10ContractTest locks column names/affinities, not SQL DEFAULT clauses (Room identity-hash catches type drift at runtime)

🤖 Generated with Claude Code

https://claude.ai/code/session_011FJZABQvMnCr3mqbk7KG9M

Stage 3 of the moderation foundation (plan: `docs/superpowers/plans/2026-07-05-moderation-ui-wrappers.md`, in-tree on this branch's session — roadmap on `docs/moderation-foundation`). **Stacked on #53** — retarget to `main` after #53 merges. **Zero user-visible behavior change.** 11 commits, 13 new files, no existing file modified. Nothing consumes these components yet; Stage 4 wires them into timeline/thread/profile/quotes/search/notifications. ### What's here - `ui/common/moderation/`: `ContentCover`, `MediaCover`, `HiddenPostRow`, `LabelBadge`, `LabelsBottomSheet`, `PostOverflowMenu`, pure `ModerationCopy` (no raw-DID leaks, tested), `ModerationPreviews` - Contract tests: cover files forbid `coil3`/`.clip(`/`graphicsLayer`/`.blur(`; media/content composed only behind the reveal gate; sheet hoisting locked out of `PostCard` - Slice-1 audit gap closure: `ModerationResolverNotificationTest` (6 tests), `SchemaV10ContractTest` (locks the v10 schema delta + migration SQL) ### Verification - `./gradlew test`: 329 unit tests, 0 failures (23 new) - `assembleDebug`: success - `lint`: only the 2 pre-existing NewApi errors in `AtProtoOAuthClient.kt` (untouched here, predate this branch) - Final whole-branch review (opus): Ready to merge — 0 Critical, 0 Important; recommended fixes applied (`Role.Button` on LabelBadge, trailing newline) ### Notes for Stage 4 wiring (do not lose) - `LabelBadge`/`LabelsBottomSheet` take `List<LabelCause>` (unstable type): callers must pass a `remember(post.uri)`-ed list or the header recomposes every pass - Wrapper `modifier` applies to the cover branch only; apply layout modifiers to the inner content, not the wrapper - `HiddenPostRow` does no internal noOverride check — callers gate eligibility - `LabelsBottomSheet` uses default sheet state (house `RepostBottomSheet` passes one explicitly) — reconcile when wiring - Previews compile but need a human glance in Android Studio (headless session) - `SchemaV10ContractTest` locks column names/affinities, not SQL DEFAULT clauses (Room identity-hash catches type drift at runtime) 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_011FJZABQvMnCr3mqbk7KG9M
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
HiddenPostRow is a slim one-line ghost row shown in place of a hidden post
(muted account or muted word). When revealed, renders the content in place,
keeping LazyColumn keys stable.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
fix: LabelBadge button semantics + ModerationCopy trailing newline
All checks were successful
peregrine-ci / assembleDebug Build succeeded (38s)
ccfae20d90
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
starsetbyte changed target branch from feat/moderation-core to main 2026-07-05 12:41:48 +00:00
Merge branch 'main' into feat/moderation-ui-wrappers
All checks were successful
peregrine-ci / assembleDebug Build succeeded (13s)
5c494aafd5
Sign in to join this conversation.
No description provided.