Skip to content

feat: link bio mentions to their dev/null profile - #1266

Merged
mnapoli merged 1 commit into
productionfrom
feat/bio-profile-links
Oct 28, 2026
Merged

mnapoli merged 1 commit into
productionfrom
feat/bio-profile-links

Conversation

@mnapoli

@mnapoli mnapoli commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

Summary

Follow-up to #1262. The @handle badges in a bio and a company now open the profile on dev/null when the account is here, a user or an organization, instead of always opening GitHub. Other handles still open GitHub in a new tab.

  • This applies to the profile page, the hovercard, and the signed-in user's sidebar.
  • Organizations only exist once someone looked them up. So when a user's GitHub profile syncs (sign-in, or the sync button in the web or API), a queued job looks up the organizations their bio and company mention.

Changes

  • UpdateMentionedOrganizations: the GitHub lookup moved out of UpdateCommitTopics, unchanged: 5 lookups at most, the 1000-request floor, a day of not-found cache, and waiting for the reset. Commits and bios share it. It now also skips handles a user has, in the same query.
  • LookUpUserOrganizations job: queued on sign-in and profile sync, only when the bio or company mentions someone. It has the same retry rules as LinkCommitTopics.
  • BioProfilesQuery: maps lowercase handle → handle for the mentions that have a profile. It is sent as bioProfiles on the profile page and in the hovercard JSON, and as auth.bioProfiles for the sidebar. It is one query on the lower(handle) index, and none when the text mentions no one.
  • BioText: links those handles with an Inertia Link to /@handle (the profile's own case), and keeps the GitHub link for the rest. The badge looks the same either way.
  • users:look-up-organizations: queues the job for existing users.

Data

No new column or table.

After deploy

Run php artisan users:look-up-organizations once, so the bios of people who don't sign in again soon get their organizations.

Validation

Run locally against a throwaway Postgres 18 with pgvector. Hosted CI is pending.

  • Pest: 4794/4794 passed, coverage exactly 100.0%, type coverage 100%.
  • PHPStan reports 0 errors; Pint passes; Rector makes no changes.
  • tsc passes, for the app and for the tests.
  • vp fmt --check resources/ is clean, and vp lint exits 0.
  • Vitest: 66 files passed, including 6 BioText tests. node --test passed.
  • Link targets, read from the DOM with Playwright on the same data:
Where Before After
Profile, @laravel (org) github.com/laravel, new tab /@laravel
Profile, @tailwindlabs (org) github.com/tailwindlabs, new tab /@tailwindlabs
Profile, @mnapoli (user) github.com/mnapoli, new tab /@mnapoli
Profile, @php-fig (not on dev/null) github.com/php-fig, new tab github.com/php-fig, new tab
Company, @laravel github.com/laravel, new tab /@laravel
Sidebar and hovercard, @laravel, @taylorotwell GitHub /@laravel, /@taylorotwell
Sidebar and hovercard, @bref (not on dev/null) GitHub GitHub
  • Layout shift, measured with PerformanceObserver:
    • first load of the profile: 0.0035 before and after, on all three runs. It already happens on production.
    • client-side visit from the @laravel badge to the organization page: 0
  • No console errors.

Visual evidence

Before is production (3a6eabe) and after is this branch. Both run locally on the same seeded data, signed in as @mnapoli.

The badges look the same; only where they lead changes. Desktop is 1440×900, laptop 1366×650 and mobile 390×844.

Profile: @laravel and @tailwindlabs are organizations, @mnapoli a user, @php-fig neither

Before, desktop light After, desktop light
Before: profile bio, desktop light After: profile bio, desktop light
After, desktop dark After, laptop light
Profile bio, desktop dark Profile bio, laptop light
Before, mobile dark After, mobile dark After, mobile light
Before: profile bio, mobile dark After: profile bio, mobile dark After: profile bio, mobile light

Clicking @laravel in the bio opens the organization page, as a client-side visit

Desktop light Desktop dark
The Laravel organization page after clicking the badge, desktop light Same, desktop dark
Mobile light Mobile dark
Same, mobile light Same, mobile dark

Hovercard and sidebar

Before, hovercard After, hovercard light After, hovercard dark
Before: hovercard bio After: hovercard bio, light After: hovercard bio, dark
Before, sidebar After, sidebar
Before: sidebar bio After: sidebar bio

Device verification

  • Mobile was tested with Chromium emulation only (390×844, touch, DPR 2), not on a real device or simulator. So the on-screen keyboard and safe areas are unverified.
  • The changed mobile interaction is tapping a bio badge. It was checked under emulation: a client-side visit to the organization page, and a fresh load of the profile.

Independent review

Independent agents reviewed the diff 3a6eabe2..9c22db7a and the captures above.

  • CODE: PASS on 9c22db7a. The first pass found that an empty bioProfiles arrives as a JSON array, so @length would have linked to /@0. That is fixed and tested. Its non-blocking nits are listed under Limitations.
  • VISUAL: PASS on the captures from 57c14696, which only differs in that empty-array case. Every before/after pair is pixel-identical. The reviewer also tabbed through the badges in both themes and found the focus ring unchanged.

Limitations

  • Mobile is emulation only.
  • The hovercard shows GitHub links for a moment: until its JSON arrives, it only has the bio, so the badges link to GitHub.
  • The backfill queues every job at once. Each job stops at the 1000-request floor and waits for the reset, so SSH key syncs keep their budget, but the burst can delay other jobs on the default queue for a while.
  • A badge inside code still opens GitHub, as before, because mentions in code don't count.

Summary by CodeRabbit

  • New Features
    • Mentions of known profiles in user bios and company details now link to their profiles, while unrecognized mentions continue to link to GitHub.
    • Organization mentions in bios and company details are looked up during sign-in and profile updates. A command is also available to queue lookups for existing users.
  • Bug Fixes
    • Organization lookups now pause and retry based on GitHub rate limits.

…ll profile

Bio and company badges open /@handle when the account is on dev/null, and GitHub
otherwise. A user's bio and company organizations are looked up when their GitHub
profile syncs, and users:look-up-organizations queues that for existing users.
@coderabbitai

coderabbitai Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 49c6b79e-129c-4be1-bdae-e21a5ecee554
Commits

Reviewing files that changed from the base of the PR and between 3a6eabe and 9c22db7.

Files selected for processing: 23
  • app/Actions/UpdateCommitTopics.php
  • app/Actions/UpdateMentionedOrganizations.php
  • app/Actions/UpdateUserProfile.php
  • app/Console/Commands/LookUpUserOrganizationsCommand.php
  • app/Http/Controllers/SessionController.php
  • app/Http/Controllers/UserController.php
  • app/Http/Controllers/UserPreviewController.php
  • app/Http/Middleware/HandleInertiaRequests.php
  • app/Jobs/LookUpUserOrganizations.php
  • app/Queries/BioProfilesQuery.php
  • resources/js/components/app-aside.tsx
  • resources/js/components/bio-text.tsx
  • resources/js/components/user-preview.tsx
  • resources/js/pages/user/show.tsx
  • resources/js/types/auth.ts
  • tests/Feature/Commands/LookUpUserOrganizationsTest.php
  • tests/Feature/Controllers/SessionControllerTest.php
  • tests/Feature/Controllers/UserControllerTest.php
  • tests/Feature/Controllers/UserPreviewControllerTest.php
  • tests/Unit/Actions/UpdateUserProfileTest.php
  • tests/Unit/BioText.test.tsx
  • tests/Unit/Jobs/LookUpUserOrganizationsTest.php
  • tests/Unit/Queries/BioProfilesQueryTest.php

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review.

Walkthrough

The pull request centralizes organization lookups and uses them for user bio and company mentions. It adds job dispatch paths and exposes matching profile handles to pages. The frontend links known profiles to dev/null and retains GitHub links for other mentions.

Changes: Organization mention lookup

Layer / File(s) Summary
Shared organization lookup
app/Actions/UpdateMentionedOrganizations.php, app/Actions/UpdateCommitTopics.php
The new action handles profile matching, lookup limits, not-found caching, rate-limit pauses, and organization updates or deletions. UpdateCommitTopics delegates its lookups to this action.
User lookup jobs
app/Jobs/LookUpUserOrganizations.php, app/Actions/UpdateUserProfile.php, app/Http/Controllers/SessionController.php, app/Console/Commands/LookUpUserOrganizationsCommand.php, tests/Feature/Commands/*, tests/Feature/Controllers/SessionControllerTest.php, tests/Unit/Actions/UpdateUserProfileTest.php, tests/Unit/Jobs/LookUpUserOrganizationsTest.php
The job extracts handles from user bios and companies and releases when the lookup action returns a retry time. Profile updates, sign-in, and the console command dispatch eligible lookups. Tests cover dispatch and lookup behavior.
Bio profile response data
app/Queries/BioProfilesQuery.php, app/Http/Controllers/UserController.php, app/Http/Controllers/UserPreviewController.php, app/Http/Middleware/HandleInertiaRequests.php, tests/Feature/Controllers/UserControllerTest.php, tests/Feature/Controllers/UserPreviewControllerTest.php, tests/Unit/Queries/BioProfilesQueryTest.php
The query maps mentioned handles to existing profiles. User pages, previews, and authenticated page props include the mapping. Tests cover matches, missing handles, and guest responses.
Mention link rendering
resources/js/components/app-aside.tsx, resources/js/components/bio-text.tsx, resources/js/components/user-preview.tsx, resources/js/pages/user/show.tsx, resources/js/types/auth.ts, tests/Unit/BioText.test.tsx
Bio and company mentions use the profile mapping to link known handles to dev/null profiles. Other mentions retain GitHub links and avatars.

Priority: Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant ProfileUpdate as UpdateUserProfile
  participant SignIn as SessionController
  participant Command as LookUpUserOrganizationsCommand
  participant Job as LookUpUserOrganizations
  participant Lookup as UpdateMentionedOrganizations
  participant GitHub
  participant Organization as UpdateOrCreateOrganization
  ProfileUpdate->>Job: dispatchFor user
  SignIn->>Job: dispatchFor user
  Command->>Job: dispatchFor eligible users
  Job->>Lookup: handle extracted handles
  Lookup->>GitHub: look up organization handles
  GitHub-->>Lookup: organization response
  Lookup->>Organization: update organization profile
Loading

Possibly related issues

  • [RFC] Orgs #938: The RFC covers organization profiles and mentions; this pull request implements organization lookup and profile links for those mentions.

Possibly related PRs

Suggested reviewers: xheaven, 0z3, nunomaduro

Merge Risk: Minimal · up to 9c22d

Profile mention links and organization lookups appear mergeable after normal checks. A lookup processes at most five handles per run, as intended.

Security architecture risk: Low · up to 9c22d

The change reuses existing organization identity, persistence, and rate-limit controls. No introduced security vulnerability was established. The main remaining uncertainty is whether additional background work could interfere with shared GitHub capacity under production concurrency.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The affected shared resources are application-wide organization profiles, organization commit-topic links, queue capacity, and GitHub API capacity. Organization discovery and SSH-key synchronization use the same GitHub client, so quota interference could extend beyond the user whose bio initiated discovery. Actual interference is not established.

Trust Boundaries and Controls

  • observed — User-controlled bio and company text is reduced to distinct lowercase handles using a constrained alphanumeric-and-hyphen pattern. These handles select fixed GitHub account endpoints, rather than attacker-supplied URLs. Browser fallback links use HTTPS GitHub URLs with noopener and noreferrer.
  • observed — Web and API profile synchronization bind to the authenticated current user and apply profile-sync throttling. Sign-in dispatch follows GitHub identity screening. Profile synchronization performs SSH-key synchronization before queuing organization discovery, whereas sign-in enqueues the two jobs independently.

Resilience and Maintainability Implications

  • observed — The shared pause uses separate cache reads, requests, and writes, rather than an atomic quota reservation. This behavior predates extraction. The five-request cap, negative cache, and freshness filtering constrain ordinary work but do not serialize concurrent jobs. Repeated sign-in already scheduled SSH-key requests before this PR, which is important counterevidence against attributing shared-quota exhaustion solely to the new lookup.
  • inferred — Missing-model handling covers users absent when queued models are restored, but does not cancel a worker that already restored the user before deletion. Such work can still perform public organization maintenance. Whether that bounded race conflicts with account-deletion policy remains unresolved; it is not evidence of user resurrection or a privileged identity write.

Hardening Proposals

  • proposed — For high-concurrency deployments, consider an atomic shared quota reservation or explicit capacity isolation for optional organization enrichment versus SSH-key synchronization. This is defense in depth for an existing advisory-pause design, not a verified vulnerability introduced by this PR.
Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 46.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the primary change: linking bio mentions to matching dev/null profiles.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 46.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 15 functions across 23 files.

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@mnapoli
mnapoli marked this pull request as ready for review October 28, 2026 18:21
@mnapoli
mnapoli merged commit 7af51f0 into production Oct 28, 2026
10 checks passed
@mnapoli
mnapoli deleted the feat/bio-profile-links branch October 28, 2026 19:50
richardkeep pushed a commit to richardkeep/dev-null.sh that referenced this pull request Oct 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant