fix: exclude inaccessible posts from channel digest generation#3941
Conversation
Channel digests could link to private posts readers cannot access: - findDigestPosts queried api.post directly with only deleted=false, bypassing the visible/banned/showOnFeed/private guarantees that feed inventory provides. Private unknown-source posts (squad-shared external URLs) entered the candidate pool via contentMeta.channels. - remapUnknownSourcePostIds kept the original private post id when no public SharePost fallback existed, producing broken Read more links in the generated digest markdown. Now candidates must be visible, not banned, shown on feed and public. Unknown-source posts are kept as candidates only so they can be remapped to a public share post, and are dropped when no accessible fallback exists.
|
🍹 The Update (preview) for dailydotdev/api/prod (at 24d7630) was successful. ✨ Neo Code ReviewRoutine image rollout (commit `9ce11899` → `c17b718a`) that fixes channel digests to suppress private/banned/hidden posts and unreachable unknown-source articles. Includes DB and Clickhouse migration jobs; a migration failure would be the only meaningful risk. ✅ Low RiskThis deployment ships a bug fix to the channel digest generation logic. The code change adds SQL filters to The migration jobs rotating from 🔵 Info — Both a DB migration job and a Clickhouse migration job are being replaced with new versions. If either migration fails, the new pods will be running code that may expect schema changes that haven't landed yet. The migration jobs run as one-shot Kubernetes Jobs ( Resource Changes Name Type Operation
~ vpc-native-clean-old-notifications-cron kubernetes:batch/v1:CronJob update
~ vpc-native-clean-gifted-plus-cron kubernetes:batch/v1:CronJob update
~ vpc-native-update-trending-cron kubernetes:batch/v1:CronJob update
~ vpc-native-validate-active-users-cron kubernetes:batch/v1:CronJob update
~ vpc-native-user-profile-updated-sync-cron kubernetes:batch/v1:CronJob update
- vpc-native-api-clickhouse-migration-9ce11899 kubernetes:batch/v1:Job delete
~ vpc-native-sync-subscription-with-cio-cron kubernetes:batch/v1:CronJob update
~ vpc-native-update-tag-materialized-views-cron kubernetes:batch/v1:CronJob update
~ vpc-native-clean-expired-better-auth-sessions-cron kubernetes:batch/v1:CronJob update
~ vpc-native-update-tags-str-cron kubernetes:batch/v1:CronJob update
~ vpc-native-user-profile-analytics-clickhouse-cron kubernetes:batch/v1:CronJob update
+ vpc-native-api-db-migration-c17b718a kubernetes:batch/v1:Job create
~ vpc-native-generic-referral-reminder-cron kubernetes:batch/v1:CronJob update
~ vpc-native-clean-zombie-user-companies-cron kubernetes:batch/v1:CronJob update
~ vpc-native-clean-channel-highlights-cron kubernetes:batch/v1:CronJob update
~ vpc-native-deployment kubernetes:apps/v1:Deployment update
+ vpc-native-api-clickhouse-migration-c17b718a kubernetes:batch/v1:Job create
- vpc-native-api-db-migration-9ce11899 kubernetes:batch/v1:Job delete
~ vpc-native-ws-deployment kubernetes:apps/v1:Deployment update
~ vpc-native-materialize-yearly-best-post-archives-cron kubernetes:batch/v1:CronJob update
~ vpc-native-personalized-digest-cron kubernetes:batch/v1:CronJob update
~ vpc-native-materialize-monthly-best-post-archives-cron kubernetes:batch/v1:CronJob update
~ vpc-native-bg-deployment kubernetes:apps/v1:Deployment update
~ vpc-native-channel-digests-cron kubernetes:batch/v1:CronJob update
~ vpc-native-post-analytics-clickhouse-cron kubernetes:batch/v1:CronJob update
~ vpc-native-generate-search-invites-cron kubernetes:batch/v1:CronJob update
~ vpc-native-temporal-deployment kubernetes:apps/v1:Deployment update
~ vpc-native-post-lifecycle-state-clickhouse-cron kubernetes:batch/v1:CronJob update
~ vpc-native-clean-zombie-users-cron kubernetes:batch/v1:CronJob update
~ vpc-native-update-highlighted-views-cron kubernetes:batch/v1:CronJob update
~ vpc-native-update-views-cron kubernetes:batch/v1:CronJob update
~ vpc-native-channel-highlights-cron kubernetes:batch/v1:CronJob update
~ vpc-native-squad-posts-analytics-refresh-cron kubernetes:batch/v1:CronJob update
~ vpc-native-rotate-daily-quests-cron kubernetes:batch/v1:CronJob update
~ vpc-native-clean-zombie-opportunities-cron kubernetes:batch/v1:CronJob update
~ vpc-native-check-analytics-report-cron kubernetes:batch/v1:CronJob update
~ vpc-native-calculate-top-readers-cron kubernetes:batch/v1:CronJob update
~ vpc-native-hourly-notification-cron kubernetes:batch/v1:CronJob update
~ vpc-native-clean-stale-user-transactions-cron kubernetes:batch/v1:CronJob update
~ vpc-native-user-posts-analytics-refresh-cron kubernetes:batch/v1:CronJob update
~ vpc-native-private-deployment kubernetes:apps/v1:Deployment update
... and 13 other changes |
Problem
Topical channel digests (e.g. the security_digest post
igABDFgDV) were generated withRead morelinks pointing to posts readers cannot access — e.g.yGw6q0Dsw, a private unknown-source article whose only share lives in a private squad.Two gaps in
src/common/channelDigest/generate.ts:findDigestPostsqueriedapi.postdirectly with onlydeleted = false+ channel match, bypassing all accessibility guarantees (visible,banned,showOnFeed,private) that the feed inventory provides elsewhere (feed.post_metadata/feed.inventoryexclude these at the view level — the digest path never goes through them).remapUnknownSourcePostIdskept the private post id when no publicSharePostfallback existed (id = fallbackId ?? post.id), so the inaccessible post id ended up in the generated markdown.Fix
findDigestPostsnow requiresvisible = true,banned = false,showOnFeed = true, andprivate = false— exceptsourceId = 'unknown'posts, which stay in the candidate pool only so they can be remapped to an accessible public share post.remapUnknownSourcePostIdsnow drops unknown-source posts that have no public share fallback instead of keeping their private id.The accessibility predicate mirrors the existing one in
fetchPublicShareFallbackPostIds(channelHighlight/queries.ts).Tests
should keep the unknown-source post id when no public SharePost existstest to assert the post is now dropped (digest returnsnullwhen nothing remains).showOnFeed=falseposts are excluded from candidates while accessible ones pass.