feat: roster signals — origin, GitHub reputation, Slack SSO, bounces - #189
Merged
Merged
Conversation
…ce webhook The roster gains origin, GitHub facts (captured at sign-in, re-probed daily), Slack SSO recency, Postmark bounces, and row-local signals with an attention tint; the private profile grows the three fields behind them; a new webhook spec covers Postmark bounces; the pipeline gains a github-probe evaluator. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LFyA5poHwrhAktrnsKrUiQ
…t probe
/user already returns created_at, public_repos, followers, following
and type; store them on the private profile on every GitHub sign-in.
probeGitHubUser() asks GET /user/{id} with the OAuth app's client
credentials — a 404 means GitHub deleted or suspended the account.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LFyA5poHwrhAktrnsKrUiQ
Best-effort private-profile write on all three assertion paths so the roster can show whether a member has actually signed into Slack. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LFyA5poHwrhAktrnsKrUiQ
POST /api/_webhooks/postmark/bounce, guarded by POSTMARK_WEBHOOK_SECRET (bearer or basic-auth password), stores terminal bounce types on the member's private profile. A hard bounce on mail we already send is the reliable "mailbox disabled" signal; no SMTP probing of our own. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LFyA5poHwrhAktrnsKrUiQ
…, signals Row-local signals (never-signed-in, no-avatar, no-bio, bio links, email/name mismatch, bounced email, GitHub gone/new/inactive) count toward an attention score; positives (slack-sso, has-footprint) are informational. Linked accounts on the requested page are re-probed against GitHub when their record is older than a day, with bounded concurrency and a failure-keeps-old-record rule. New origin filter. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LFyA5poHwrhAktrnsKrUiQ
Signed-up-here / Imported, GitHub (linked, with age, repos, followers; red when gone; amber when new or inactive), Slack SSO recency, bounced email, email-vs-name mismatch, links in bio, no bio / no avatar. Rows tint amber at three negative signals and red at five or on a critical one. Default filter is "no vote yet"; j/k/s/n/Enter drive triage. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LFyA5poHwrhAktrnsKrUiQ
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LFyA5poHwrhAktrnsKrUiQ
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LFyA5poHwrhAktrnsKrUiQ
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements
plans/roster-signals.md. Follow-up to #187 from the first day on/admin/members: the roster now says what a careful reader would conclude, and it carries the signals that matter most for spam arriving through GitHub.Roster
Signed up here/Imported(origin);GitHub · @login · age · repos · followerslinking to the profile (red "GitHub account gone" when GitHub 404s the account; amber when < 30 days old or zero activity);Slack · {when};Email bounced · {type};email ≠ name;{n} links in bio;no bio;no avatar.j/k/s/n/Enterkeyboard triage.Data behind it (all on the private profile)
created_at,public_repos,followers,following,type) — we were already fetching them and throwing them away.GET /user/{id}with the OAuth app's client credentials for linked rows on the page whose record is older than a day (bounded concurrency; a failed probe keeps the old record).status = gone⇒ GitHub deleted or suspended the account.lastSlackSsoAtstamped on every assertion the IdP issues.POST /api/_webhooks/postmark/bounce(POSTMARK_WEBHOOK_SECRET, bearer or basic-auth) records terminal bounce types — no SMTP probing.Pipeline
probe-githubevaluator in the private repo (codeforphilly-spam-detection): 404 →spam0.95; account > 1y with repos/followers →legit0.9 (protects from every machine verdict); elseuncertain.Validation
type-check+lintclean; API 39 files / 473 tests (new: signals units, probe gating/persistence, webhook auth + mapping, SSO stamp); web 28 / 124.To turn on in prod
POSTMARK_WEBHOOK_SECRETintocodeforphilly-secretsand add the webhook URL in Postmark with basic auth.GITHUB_OAUTH_CLIENT_ID/SECRETto the private repo's.envforprobe-github. The roster probe uses the same OAuth app credentials already sealed for sign-in.🤖 Generated with Claude Code
https://claude.ai/code/session_01LFyA5poHwrhAktrnsKrUiQ