tidaldb/.sdlc/features/m9-leave-revocation/design.md

62 lines
2.1 KiB
Markdown

# Design: Leave & Stop-Forward (m9-leave-revocation)
## Architecture
Backend-only. No UI changes. Touches 6 files.
## Storage
**Tag::CommunityLeave = 0x11** added to `storage/keys.rs` (after PurgeManifest = 0x10).
Key format: `encode_key(EntityId::new(user_id), Tag::CommunityLeave, b"")` — no suffix.
Value format: `[status: 1 byte (0=Active, 1=Left)][left_at_ns: 8 bytes LE]` — fixed 9 bytes.
Stored in the **users** storage partition via `storage.users_engine()`.
## Data Types (entities/community.rs)
Appended to existing `CommunityMembershipIndex`:
```rust
pub enum MembershipStatus { Active, Left }
pub struct CommunityMembership { user_id, status, left_at_ns: Option<u64> }
pub fn serialize_membership(m: &CommunityMembership) -> [u8; 9]
pub fn deserialize_membership(user_id: u64, bytes: &[u8]) -> Option<CommunityMembership>
```
## TidalDb Field (db/mod.rs)
```rust
community_leave_status: dashmap::DashMap<u64, crate::entities::CommunityMembership>,
```
Added after `community_membership`. Initialized to `DashMap::new()` in both `from_config` and `from_parts`.
## API (db/community.rs)
Three new public methods appended to the existing `TidalDb` impl:
- `leave_community_layer(user_id)` — creates Left membership, persists, inserts into DashMap
- `rejoin_community_layer(user_id)` — creates Active membership (preserving left_at_ns), persists, inserts
- `community_layer_status(user_id)` — reads from DashMap, returns default_active if absent
- `persist_leave_record(membership)` — private helper: writes to users_engine via Tag::CommunityLeave
## Gate (db/signals.rs)
Added to BOTH `try_cohort_attribution` and `try_community_forwarding`:
```rust
if self.community_leave_status.get(&user_id)
.map_or(false, |m| m.status == MembershipStatus::Left)
{
return;
}
```
Placed at top of each function before any other work.
## Startup Restore (db/state_rebuild.rs)
`rebuild_community_leave_status(storage, map)` scans users_engine for Tag::CommunityLeave keys, deserializes each, inserts into DashMap. Called from `from_parts` after `rebuild_community_memberships`.