Skip to content

feat(membership): add inactive member detection#1061

Open
avivkeller wants to merge 2 commits intonodejs:mainfrom
avivkeller:get-inactive-members
Open

feat(membership): add inactive member detection#1061
avivkeller wants to merge 2 commits intonodejs:mainfrom
avivkeller:get-inactive-members

Conversation

@avivkeller
Copy link
Copy Markdown
Member

@avivkeller avivkeller commented Apr 24, 2026

@JakobJingleheimer mentioned that during the collaborator's summit, it was discussed that we should be more wary of the size of the organization. This adds a daily workflow to keep a constant "activity monitor" issue open.

It requires that a public_repo, read:org token be made for this repository.

An example issue that this workflow generates is below (Note: In a real issue, these users would be pinged):

Workflow Output
# Organization Inactivity Audit — nodejs

_Run: 2026-04-26T16:11:12.618Z · Window: last 90 days · Inactive: 125/254_

## Summary
- Inactive members: 125
- Members with no team: 9
- Members with no recent activity: 122
- Both signals: 6
## Inactive members

| Login | Reason | Last seen | Teams |
|---|---|---|---|
| @a0viedo | no-activity | — | `documentation`, `all-members` |
| @AdamBraden | no-activity | — | `platform-windows`, `platforms`, `core`, `platform-windows-arm`, `platform-windows-arm-testing`, `all-members` |
| @agnat | no-activity | — | `addon-api`, `coreworkinggroups`, `all-members` |
| @ahmadawais | no-activity | — | `next-10`, `all-members` |
| @aixtools | no-team | — | — |
| @aks- | no-activity | — | `http`, `core`, `all-members` |
| @alexcfyung | no-activity | — | `platforms`, `core`, `platform-zos`, `all-members` |
| @alexeykuzmin | no-activity | — | `delivery-channels`, `embedders`, `all-members` |
| @alexweej | no-team | — | — |
| @amiller-gh | no-activity | — | `commit-queue`, `all-members` |
| @anandsuresh | no-activity | — | `platform-freebsd`, `platforms`, `core`, `all-members` |
| @andrewbranch | no-activity | — | `typescript`, `all-members` |
| @AnnaMag | no-activity | — | `v8`, `core`, `vm`, `all-members` |
| @ashishkurmi | no-activity | — | `security-wg`, `all-members` |
| @bahamat | no-activity, no-team | — | — |
| @bajtos | no-activity | — | `all-members`, `wintercg` |
| @bmeurer | no-activity | — | `v8`, `performance`, `core`, `all-members` |
| @bradleythughes | no-activity | — | `platform-freebsd`, `platforms`, `core`, `all-members` |
| @bzoz | no-activity | — | `platform-windows`, `node-gyp`, `platforms`, `core`, `all-members` |
| @camillobruni | no-activity | — | `v8`, `core`, `all-members` |
| @charlespierce | no-activity, no-team | — | — |
| @coreybutler | no-activity | — | `version-management`, `delivery-channels`, `all-members` |
| @coreyfarrell | no-activity | — | `tooling`, `all-members` |
| @DavidCai1111 | no-activity | — | `testing`, `all-members` |
| @davidmarkclements | no-activity | — | `diagnostics`, `post-mortem`, `coreworkinggroups`, `all-members` |
| @davisjam | no-activity | — | `benchmarking`, `coreworkinggroups`, `all-members` |
| @designMoreWeb | no-activity | — | `electron-installer`, `next-10`, `all-members` |
| @dgonzalez | no-activity | — | `security-wg`, `all-members` |
| @ehsan | no-activity | — | `spidernode`, `all-members` |
| @emaste | no-activity | — | `platform-freebsd`, `platforms`, `core`, `all-members` |
| @Emuentes | no-activity | — | `package-maintenance`, `all-members` |
| @Eomm | no-activity | — | `package-maintenance`, `all-members` |
| @esarafianou | no-activity | — | `security-wg`, `all-members` |
| @evocateur | no-activity | — | `tooling`, `all-members` |
| @fed135 | no-activity | — | `all-members`, `web-server-frameworks` |
| @felixrieseberg | no-activity | — | `electron-installer`, `delivery-channels`, `embedders`, `all-members` |
| @fraxken | no-activity | — | `security-wg`, `all-members` |
| @fritzy | no-activity | — | `npm`, `core`, `all-members` |
| @gareth-ellis | no-activity | — | `benchmarking`, `performance`, `coreworkinggroups`, `core`, `all-members` |
| @gdams | no-activity | — | `testing`, `platform-macos`, `platforms`, `core`, `commit-queue`, `all-members` |
| @geek | no-activity | — | `platform-smartos`, `platforms`, `core`, `all-members` |
| @ghinks | no-activity | — | `package-maintenance`, `next-10`, `all-members` |
| @GnorTech | no-activity | — | `delivery-channels`, `embedders`, `all-members` |
| @groundwater | no-activity | — | `delivery-channels`, `embedders`, `all-members` |
| @gsathya | no-activity | — | `v8`, `core`, `all-members` |
| @Harkunwar | no-activity | — | `web`, `nodejs-website`, `all-members` |
| @hashseed | no-activity | — | `v8`, `core`, `v8-update`, `all-members` |
| @HinataKah0 | no-team | — | — |
| @hiroppy | no-activity | — | `core`, `path`, `console`, `all-members` |
| @hmalphettes | no-activity, no-team | — | — |
| @iansu | no-activity | — | `core`, `linting`, `tooling`, `package-maintenance`, `next-10`, `all-members` |
| @iarna | no-activity | — | `tooling`, `all-members` |
| @IgorTodorovskiIBM | no-activity | — | `platforms`, `core`, `platform-zos`, `all-members` |
| @imyller | no-activity | — | `platform-macos`, `platforms`, `core`, `all-members` |
| @jasonkarns | no-activity | — | `version-management`, `delivery-channels`, `all-members` |
| @jBarz | no-activity | — | `v8`, `core`, `all-members` |
| @jbergstroem | no-activity | — | `platform-freebsd`, `github-bot`, `platforms`, `core`, `all-members` |
| @JckXia | no-activity | — | `node-api`, `all-members` |
| @JiaLiPassion | no-activity | — | `async_hooks`, `core`, `all-members` |
| @jkleinsc | no-activity | — | `delivery-channels`, `embedders`, `all-members` |
| @jlenon7 | no-activity | — | `loaders`, `all-members` |
| @joaocgreis | no-activity | — | `build`, `platform-windows`, `jenkins-release-admins`, `node-gyp`, `platforms`, `coreworkinggroups`, `core`, `jenkins-admins`, `build-infra`, `all-members` |
| @JoeDoyle23 | no-activity | — | `electron-installer`, `all-members` |
| @joesepi | no-activity | — | `security`, `tooling`, `next-10`, `all-members`, `security-stewards` |
| @john-yan | no-activity | — | `platform-s390`, `platform-aix`, `platform-ppc`, `platforms`, `core`, `all-members` |
| @JohnTitor | no-activity | — | `moderation`, `i18n`, `all-members` |
| @jperkin | no-activity | — | `platform-smartos`, `platforms`, `core`, `all-members` |
| @jschlight | no-activity | — | `node-api`, `all-members` |
| @karenyavine | no-activity | — | `security-wg`, `all-members` |
| @kjin | no-activity | — | `diagnostics`, `coreworkinggroups`, `core`, `perfetto`, `all-members` |
| @kunalspathak | no-activity | — | `benchmarking`, `coreworkinggroups`, `all-members` |
| @lance | no-activity | — | `platform-macos`, `platforms`, `core`, `all-members` |
| @larson-carter | no-activity, no-team | — | — |
| @lholmquist | no-activity | — | `package-maintenance`, `all-members` |
| @littledivy | no-activity | — | `node-api-implementer`, `all-members` |
| @lucamaraschi | no-activity | — | `diagnostics`, `post-mortem`, `coreworkinggroups`, `all-members` |
| @mafintosh | no-activity | — | `core`, `os`, `all-members` |
| @manishprivet | no-activity | — | `web`, `nodejs-website`, `all-members` |
| @marcelklehr | no-activity | — | `version-management`, `delivery-channels`, `all-members` |
| @MarcinHoppe | no-activity | — | `security-wg`, `all-members` |
| @mattpocock | no-activity, no-team | — | — |
| @MayaLekova | no-activity | — | `v8`, `core`, `all-members` |
| @mgalexander | no-activity | — | `security-wg`, `all-members` |
| @mike-kaufman | no-activity | — | `diagnostics`, `coreworkinggroups`, `all-members` |
| @MoonBall | no-activity | — | `streams`, `http-parser`, `http`, `coreworkinggroups`, `core`, `all-members` |
| @natorion | no-activity | — | `v8`, `core`, `all-members` |
| @nitsakh | no-activity | — | `delivery-channels`, `embedders`, `all-members` |
| @niyas-sait | no-activity | — | `platforms`, `core`, `platform-windows-arm`, `platform-windows-arm-testing`, `all-members` |
| @nlf | no-activity | — | `npm`, `core`, `all-members` |
| @node-forward-build | no-activity | — | `build`, `jenkins-release-admins`, `coreworkinggroups`, `jenkins-admins`, `all-members` |
| @nornagon | no-activity | — | `delivery-channels`, `embedders`, `all-members` |
| @nullivex | no-activity | — | `version-management`, `delivery-channels`, `all-members` |
| @o- | no-activity | — | `v8`, `core`, `all-members` |
| @obensource | no-activity | — | `i18n-api`, `i18n`, `all-members` |
| @ospencer | no-activity | — | `wasi`, `all-members` |
| @othiym23 | no-activity | — | `diagnostics`, `coreworkinggroups`, `moderation`, `all-members` |
| @pavelfeldman | no-activity | — | `v8-inspector`, `all-members` |
| @phated | no-activity | — | `wasi`, `all-members` |
| @phillipj | no-activity | — | `github-bot`, `all-members` |
| @piccoloaiutante | no-activity | — | `streams-admins`, `all-members` |
| @potham | no-activity | — | `package-maintenance`, `all-members` |
| @puzpuzpuz | no-activity | — | `benchmarking`, `async_hooks`, `coreworkinggroups`, `core`, `next-10`, `all-members` |
| @pxlpnk | no-activity | — | `security-wg`, `all-members` |
| @rexagod | no-activity | — | `streams`, `http`, `testing`, `coreworkinggroups`, `core`, `all-members` |
| @robertgzr | no-activity | — | `single-executable`, `all-members` |
| @rogerwang | no-activity | — | `delivery-channels`, `embedders`, `all-members` |
| @RReverser | no-activity | — | `buffer`, `core`, `all-members` |
| @rubys | no-activity | — | `documentation`, `core`, `repl`, `all-members` |
| @sampsongao | no-activity | — | `n-api-admins`, `all-members` |
| @shanpriyan | no-activity, no-team | — | — |
| @shigeki | no-activity | — | `http2`, `core`, `all-members` |
| @SomeoneWeird | no-activity | — | `security-wg`, `all-members` |
| @stduhpf | no-activity | — | `caritat`, `all-members` |
| @ThePrez | no-activity | — | `platforms`, `core`, `platform-ibmi`, `all-members` |
| @ThePrimeagen | no-activity | — | `benchmarking`, `coreworkinggroups`, `all-members` |
| @TooTallNate | no-activity | — | `addon-api`, `coreworkinggroups`, `all-members` |
| @uttampawar | no-activity | — | `benchmarking`, `coreworkinggroups`, `all-members` |
| @verwaest | no-activity | — | `v8`, `core`, `all-members` |
| @victorgomes | no-activity | — | `v8`, `core`, `all-members` |
| @vostrik | no-activity | — | `platform-windows`, `platforms`, `core`, `package-maintenance`, `all-members` |
| @vvalderrv | no-activity | — | `all-members`, `linuxit-infra-temp` |
| @yhwang | no-activity | — | `testing`, `all-members` |
| @yosuke-furukawa | no-activity | — | `http2`, `core`, `all-members` |
| @yunong | no-activity | — | `diagnostics`, `post-mortem`, `coreworkinggroups`, `all-members` |
| @zsw007 | no-activity | — | `platforms`, `core`, `platform-zos`, `all-members` |

## Errors
- @deian — Validation Failed: {"message":"The listed users cannot be searched either because the users do not exist or you do not have permission to view the users.","resource":"Search","field":"q","code":"invalid"} - https://docs.github.com/v3/search/
- @ebraminio — Validation Failed: {"message":"The listed users cannot be searched either because the users do not exist or you do not have permission to view the users.","resource":"Search","field":"q","code":"invalid"} - https://docs.github.com/v3/search/
- @nodejs-crowdin — Validation Failed: {"message":"The listed users cannot be searched either because the users do not exist or you do not have permission to view the users.","resource":"Search","field":"q","code":"invalid"} - https://docs.github.com/v3/search/
- @ronperris — Validation Failed: {"message":"The listed users cannot be searched either because the users do not exist or you do not have permission to view the users.","resource":"Search","field":"q","code":"invalid"} - https://docs.github.com/v3/search/

Copilot AI review requested due to automatic review settings April 24, 2026 19:07
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an automated “inactive member” monitor for the org by running a scheduled GitHub Actions workflow that scans org membership/team data and posts (or updates) a report issue in this repository.

Changes:

  • Added a new Node.js script to fetch org members/teams, check recent activity, and render a Markdown report.
  • Added a daily scheduled GitHub Actions workflow to run the script using a user token.
  • Updated the access-token registry documentation to include the new secret entry.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 10 comments.

File Description
tools/get-inactive-members.mjs Implements org member/team fetch, activity classification, report rendering, and issue upsert logic.
.github/workflows/get-inactive-members.yml Schedules and runs the new script via actions/github-script using GH_USER_TOKEN.
request-an-access-token.md Documents the new GH_USER_TOKEN usage/registry entry for nodejs/admin.
Comments suppressed due to low confidence (1)

tools/get-inactive-members.mjs:513

  • The report issue is only created/updated when inactive.length > 0. This means (1) the rendered “No inactive members found” state is never posted, and (2) an existing report issue won’t be updated if the org becomes fully active—leaving stale results in the issue body. Upsert the issue unconditionally so the report always reflects the latest scan.

    core.info(`Report available at: ${html_url}`);
  }
}


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread request-an-access-token.md
Comment thread request-an-access-token.md Outdated
Comment thread .github/workflows/get-inactive-members.yml Outdated
Comment thread tools/get-inactive-members.mjs Outdated
Comment thread tools/get-inactive-members.mjs Outdated
Comment thread tools/get-inactive-members.mjs Outdated
Comment thread tools/get-inactive-members.mjs Outdated
Comment thread .github/workflows/get-inactive-members.yml Outdated
Comment thread .github/workflows/get-inactive-members.yml Outdated
Comment thread tools/get-inactive-members.mjs Outdated
@JakobJingleheimer
Copy link
Copy Markdown
Member

Thanks Aviv! I was gonna write out the details tomorrow with a couple options.

Are you familiar with the consensus reached?

@avivkeller
Copy link
Copy Markdown
Member Author

Are you familiar with the consensus reached?

I am not, I've been away, can you summarize?

@JakobJingleheimer
Copy link
Copy Markdown
Member

Yeah, I shall do tomorrow 🙂

@JakobJingleheimer
Copy link
Copy Markdown
Member

I think the "no activity" criteria should be adjusted:

  • 3 months instead of 12
    • 6 months to remove, but the 3 months so there's a "warning"
  • exclude collaborators (that's a different check), soon to be called "maintainers".

The rest is a wishlist. What is here now is a good v1.0.0.

With the new team-based setup for Core, that will need some special handling because there are 1–2 repos that need to be checked (nodejs/node and the relevant team's repo); for Core activity, the check should be narrowed to issues & PRs labeled with that subsystem's label (activity for team A doesn't count for team B).

It would ideally also check meeting minutes for attendees; facilitating that check may require meeting minutes to adhere to a partially prescribed/standard format, and them to be stored in a standard place (eg nodejs/test-runner/tree/main/minutes).

For the report, it would ideally break down to which team(s) the member is inactive, as well as call out when the member is completely inactive across the whole org. The reports would ideally also specify how long each member has been inactive (in a human-friendly way, like 3.5 months).

I think it would be good to open:

  • a "parent" issue on nodejs/admin with the full report
    • TSC would remove fully inactive members from the org
    • probably tag @nodejs/tsc? (up to them—that could maybe get annoying?)
  • a team issue on each team's repo when it has inactive members
    • teams are tasked with managing their members (so the issue alerts them to cull their inactive member(s))
    • probably tag the team's maintainer sub-team (eg @nodejs/core/test-runner/maintainers); all maintainer sub-teams will have the same suffix (eg …/maintainers)

@avivkeller avivkeller closed this Apr 26, 2026
@avivkeller avivkeller force-pushed the get-inactive-members branch from 2a2fbfc to 43bc15c Compare April 26, 2026 16:38
@avivkeller avivkeller reopened this Apr 26, 2026
@avivkeller
Copy link
Copy Markdown
Member Author

@JakobJingleheimer Right now, I have it stored at https://github.com/avivkeller/organization-auditor/ due to the complexity, but it:

Considers a user as organization-inactive if they haven't committed to a single repository in 3 months
Considers a user as organization-inactive if they are a member of no teams
Considers a user as team-inactive if they haven't contributed to a team repository in 3 months.

@avivkeller
Copy link
Copy Markdown
Member Author

We'll need to add a "team map" to this repository first, so it knows where to open the issue for individual team inactivity, i.e.
team-map: >
{
"issue-triage": "nodejs/node",
}

@JakobJingleheimer
Copy link
Copy Markdown
Member

Sweet, thanks! Looks pretty good :)

A couple questions:

  • vitest instead of node:test? 🥺
  • github/issue.ts I see it's "upserting"; it's not upserting the same single issue very time a report is generated, right? (I think it should open a new issue each time it a report is generated)

We'll need to add a "team map" to this repository first

I wonder if there's a more dynamic way to handle this 🤔 What if team repos and team names always matched? Would the web team (and others) mind if their name was changed from @nodejs/web@nodejs/nodejs.org? It might get also tricky with the new Core team because the subsystem team name will be eg @nodejs/core/test-runner (and its repo nodejs/test-runner).

Multiple sources of truth is perhaps also a small concern: nodejs/node will have much of this info in a Code Owners file; if that info is also here, they're not connected so they could become desynchronised.

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.

3 participants