feat: maintainer toolset β scheduled triage sweep + daily Discord scorecard#950
feat: maintainer toolset β scheduled triage sweep + daily Discord scorecard#950tombeckenham wants to merge 6 commits into
Conversation
β¦recard Adds two stateless, API-only scheduled workflows driven by a fixture-tested TypeScript toolkit in scripts/maintainer/: - maintainer-sweep.yml (every 3h): routes and assigns open PRs/issues to maintainers via .github/maintainers.json area globs with least-loaded rotation and per-maintainer caps, posts a one-time ack comment with a deterministic pre-review checklist (CI, conflicts, changeset, E2E), requests reproductions on bug reports lacking one, and reconciles waiting-on / ready-to-merge / needs-repro / has-pr labels. Suspected drive-by/bounty PRs (new account + trivial diff + no linked issue) are left untouched for human judgment. - maintainer-scorecard.yml (daily): posts a to-do digest to Discord β SLA breaches (24h first response / 48h follow-up), ready-to-merge PRs, per-maintainer queues with fresh contributor replies, new/flagged/stale items, unanswered discussions, same-author PR clusters, and stats (open deltas, first-response median/p90, merges, pending changesets). All metrics are recomputed from GitHub timelines each run (no stored state); idempotency comes from HTML markers in bot comments plus visible assignment/label state. PR code is never checked out or executed β no pull_request_target. Unit tests wire into CI via the root test:maintainer nx target. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Note Currently processing new changes in this PR. This may take a few minutes, please wait... βοΈ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: π Files selected for processing (12)
π WalkthroughWalkthroughIntroduces a maintainer automation toolset that collects GitHub repository data, classifies and routes work, plans or executes sweep mutations, computes scorecards, publishes Discord digests, and runs through scheduled or manual GitHub Actions workflows. ChangesMaintainer automation
Estimated code review effort: 5 (Critical) | ~120 minutes π₯ Pre-merge checks | β 4 | β 1β Failed checks (1 warning)
β Passed checks (4 passed)
β¨ Finishing Touchesπ Generate docstrings
π§ͺ Generate unit tests (beta)
βοΈ Resolve merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
π Changeset Version PreviewNo changeset entries found. Merging this PR will not cause a version bump for any packages. |
|
View your CI Pipeline Execution β for commit fac410b
βοΈ Nx Cloud last updated this comment at |
@tanstack/ai
@tanstack/ai-acp
@tanstack/ai-angular
@tanstack/ai-anthropic
@tanstack/ai-bedrock
@tanstack/ai-claude-code
@tanstack/ai-client
@tanstack/ai-code-mode
@tanstack/ai-code-mode-skills
@tanstack/ai-codex
@tanstack/ai-devtools-core
@tanstack/ai-elevenlabs
@tanstack/ai-event-client
@tanstack/ai-fal
@tanstack/ai-gemini
@tanstack/ai-grok
@tanstack/ai-grok-build
@tanstack/ai-groq
@tanstack/ai-isolate-cloudflare
@tanstack/ai-isolate-node
@tanstack/ai-isolate-quickjs
@tanstack/ai-mcp
@tanstack/ai-mistral
@tanstack/ai-ollama
@tanstack/ai-openai
@tanstack/ai-opencode
@tanstack/ai-openrouter
@tanstack/ai-preact
@tanstack/ai-react
@tanstack/ai-react-ui
@tanstack/ai-sandbox
@tanstack/ai-sandbox-cloudflare
@tanstack/ai-sandbox-daytona
@tanstack/ai-sandbox-docker
@tanstack/ai-sandbox-local-process
@tanstack/ai-sandbox-sprites
@tanstack/ai-sandbox-vercel
@tanstack/ai-solid
@tanstack/ai-solid-ui
@tanstack/ai-svelte
@tanstack/ai-utils
@tanstack/ai-vue
@tanstack/ai-vue-ui
@tanstack/openai-base
@tanstack/preact-ai-devtools
@tanstack/react-ai-devtools
@tanstack/solid-ai-devtools
commit: |
β¦ation The team doesn't split by code area, so drop the guessed area globs from maintainers.json. Assignment is now pure least-loaded rotation; `areas` stays supported as an optional per-maintainer routing priority. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Issue/PR titles are attacker-controlled and were interpolated verbatim into Discord embed descriptions, so a title like "[urgent: click](https://evil.com)" rendered as a masked link in the maintainer channel. shortTitle() now backslash-escapes Discord markdown metacharacters after truncation; the bot's own [#N](url) links are built separately and still render. Also simplifies a redundant type assertion in the GraphQL error check. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 13
π€ Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/maintainer-scorecard.yml:
- Around line 30-31: Update permissions in
.github/workflows/maintainer-scorecard.yml at lines 30-31 to add issues: read,
pull-requests: read, and discussions: read alongside contents: read. Update
permissions in .github/workflows/maintainer-sweep.yml at lines 28-31 to add
discussions: read while preserving its existing issues: write and pull-requests:
write permissions.
In `@scripts/maintainer/classify.ts`:
- Around line 170-176: Update changesRequestedOutstanding to use the latest
CHANGES_REQUESTED review and compare only PR-author comments or commits
occurring after that review; stop relying on aggregate lastContributorActivityAt
and lastMaintainerResponseAt timestamps. Preserve the outstanding state when no
author activity follows the requesting review, and add regression coverage for
intervening third-party and maintainer comments.
- Around line 165-168: Update the readyToMerge calculation in
scripts/maintainer/classify.ts to require explicitly successful CI and
explicitly mergeable pull-request states, rather than only excluding failure and
conflict states. Preserve the existing approval and non-draft requirements, and
add negative tests covering pending CI and unknown mergeability.
- Around line 135-142: Update hasMarkerComment to consider a marker only when
the timeline event is an automation-generated comment, while preserving the
existing marker-body matching. Use the eventβs existing author or automation
identity field to exclude contributor comments before checking
e.body.includes(marker).
In `@scripts/maintainer/collect.ts`:
- Around line 125-130: Update the snapshot collection logic around the GraphQL
connections files, closingIssuesReferences, timelineItems, and discussion
comments so capped results are not treated as complete data. Propagate each
connectionβs pagination/completeness metadata through the collected snapshot,
and ensure downstream triage automation either fetches all pages or suppresses
decisions when any required connection is truncated.
In `@scripts/maintainer/config.ts`:
- Around line 18-20: Update the validation guarding repo in the configuration
flow to require exactly one slash separating two non-empty owner and name
components, rejecting values such as owner/, /repo, and owner/repo/extra before
collectSnapshot receives them.
In `@scripts/maintainer/discord.ts`:
- Around line 201-209: Bound the webhook request in the chunkEmbeds loop by
adding an AbortController-based timeout to the fetchImpl call, passing its
signal and clearing the timer when the request settles. Handle timeout or abort
failures explicitly so the scheduled workflow exits through the intended failure
path, and add coverage for the stalled-request case.
- Around line 201-209: Update the webhook payload in the embed-sending loop to
place maintainer mentions in the top-level content field rather than only inside
embed descriptions. Use the existing mention-related symbols to build that
content for alerting messages, and set allowed_mentions.users to exactly the
corresponding maintainer IDs while preserving the existing embed chunking and
POST behavior.
- Around line 116-123: Update the embed construction around flaggedLines and the
βNew in the last 24hβ title to place flagged entries before newLines when
building the section, and include the flagged count in the title alongside the
new item count. Preserve the existing flagged-line formatting and section
truncation behavior.
In `@scripts/maintainer/github.ts`:
- Around line 15-19: Update GitHubClientOptions and the request paths in the
GitHub client to support an injectable timeout, using it to abort every GitHub
fetch when the deadline expires. Ensure the timeout applies consistently to both
API paths while preserving the existing fetchImpl and baseUrl behavior.
In `@scripts/maintainer/metrics.ts`:
- Around line 238-246: Update the assignment-coverage calculation around
assignablePRs and pctPRsAssigned to exclude PRs marked suspectedSpam from the
denominator, while preserving the existing draft exclusion and assigned-count
logic. In scripts/maintainer/README.md lines 5-9, clarify that assignment
applies only to eligible items and document the cap/flag exceptions.
- Around line 224-235: Update the recentlyClosed loop to exclude items whose
author matches the configured roster, using the same case-insensitive
rosterAuthor check already applied to open samples. Extend the existing
early-continue condition alongside authorIsBot and the weekAgo cutoff, while
preserving response state derivation for eligible items.
- Around line 126-131: Update the freshReplies filter to require an explicit
contributor-after-maintainer transition for both pull requests and issues,
rather than treating every item with waitingOn === 'maintainer' as fresh. Use
the existing freshContributorReply state where available and add equivalent
transition tracking to the issue data before filtering; preserve only items that
actually represent a new contributor reply.
πͺ Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
βΉοΈ Review info
βοΈ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 6551ce8e-7d7d-43cd-a602-daf6e7ef787f
π Files selected for processing (24)
.github/maintainers.json.github/workflows/maintainer-scorecard.yml.github/workflows/maintainer-sweep.ymlpackage.jsonscripts/maintainer/README.mdscripts/maintainer/actions.tsscripts/maintainer/classify.test.tsscripts/maintainer/classify.tsscripts/maintainer/collect.tsscripts/maintainer/comments.tsscripts/maintainer/config.tsscripts/maintainer/discord.tsscripts/maintainer/env.tsscripts/maintainer/fixtures.tsscripts/maintainer/github.tsscripts/maintainer/glob.tsscripts/maintainer/metrics.test.tsscripts/maintainer/metrics.tsscripts/maintainer/route.test.tsscripts/maintainer/route.tsscripts/maintainer/scorecard.tsscripts/maintainer/sweep.test.tsscripts/maintainer/sweep.tsscripts/maintainer/types.ts
| const hasConflicts = pr.mergeable === 'CONFLICTING' | ||
| const ciFailing = pr.ciState === 'failure' | ||
| const approved = pr.reviewDecision === 'APPROVED' | ||
| const readyToMerge = approved && !hasConflicts && !ciFailing && !pr.isDraft |
There was a problem hiding this comment.
π― Functional Correctness | π Major | β‘ Quick win
Require explicitly successful CI and mergeability before marking ready.
Line 168 treats pending/unknown CI and mergeability as ready because it only excludes known failure and conflict states. This can publish non-mergeable PRs in the ready queue.
Proposed fix
const approved = pr.reviewDecision === 'APPROVED'
-const readyToMerge = approved && !hasConflicts && !ciFailing && !pr.isDraft
+const readyToMerge =
+ approved &&
+ pr.mergeable === 'MERGEABLE' &&
+ pr.ciState === 'success' &&
+ !pr.isDraftAdd negative tests for pending CI and unknown mergeability.
π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const hasConflicts = pr.mergeable === 'CONFLICTING' | |
| const ciFailing = pr.ciState === 'failure' | |
| const approved = pr.reviewDecision === 'APPROVED' | |
| const readyToMerge = approved && !hasConflicts && !ciFailing && !pr.isDraft | |
| const hasConflicts = pr.mergeable === 'CONFLICTING' | |
| const ciFailing = pr.ciState === 'failure' | |
| const approved = pr.reviewDecision === 'APPROVED' | |
| const readyToMerge = | |
| approved && | |
| pr.mergeable === 'MERGEABLE' && | |
| pr.ciState === 'success' && | |
| !pr.isDraft |
π€ Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/maintainer/classify.ts` around lines 165 - 168, Update the
readyToMerge calculation in scripts/maintainer/classify.ts to require explicitly
successful CI and explicitly mergeable pull-request states, rather than only
excluding failure and conflict states. Preserve the existing approval and
non-draft requirements, and add negative tests covering pending CI and unknown
mergeability.
| // Outstanding changes-requested: the decision stands and the contributor | ||
| // hasn't commented or pushed since the last maintainer response. | ||
| const changesRequestedOutstanding = | ||
| pr.reviewDecision === 'CHANGES_REQUESTED' && | ||
| (state.lastContributorActivityAt === null || | ||
| (state.lastMaintainerResponseAt !== null && | ||
| state.lastContributorActivityAt <= state.lastMaintainerResponseAt)) |
There was a problem hiding this comment.
π― Functional Correctness | π Major | β‘ Quick win
Base outstanding change requests on the requesting review and PR author.
These aggregate timestamps conflate unrelated participants and maintainer comments with the review lifecycle. For example, an author can push after a change request, then a later maintainer comment makes the PR wait on the author again while reviewDecision remains CHANGES_REQUESTED.
Compare author comments/commits against the latest changes-requested review instead, and add regression coverage for intervening third-party and maintainer comments.
π€ Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/maintainer/classify.ts` around lines 170 - 176, Update
changesRequestedOutstanding to use the latest CHANGES_REQUESTED review and
compare only PR-author comments or commits occurring after that review; stop
relying on aggregate lastContributorActivityAt and lastMaintainerResponseAt
timestamps. Preserve the outstanding state when no author activity follows the
requesting review, and add regression coverage for intervening third-party and
maintainer comments.
| labels(first: 20) { nodes { name } } | ||
| assignees(first: 10) { nodes { login } } | ||
| files(first: 100) { nodes { path } } | ||
| closingIssuesReferences(first: 10) { nodes { number } } | ||
| commits(last: 1) { nodes { commit { statusCheckRollup { state } } } } | ||
| timelineItems(last: 100, itemTypes: [ISSUE_COMMENT, PULL_REQUEST_REVIEW, PULL_REQUEST_COMMIT]) { |
There was a problem hiding this comment.
ποΈ Data Integrity & Integration | π Major | ποΈ Heavy lift
π§© Analysis chain
π Script executed:
#!/bin/bash
set -euo pipefail
# Locate the collector and relevant consumers
git ls-files | rg '^(scripts/maintainer/collect\.ts|src/|scripts/)' || true
echo '--- collect.ts outline ---'
ast-grep outline scripts/maintainer/collect.ts --view expanded || true
echo '--- search for normalized completeness metadata / truncation handling ---'
rg -n "complete|completeness|truncat|pageInfo|hasNextPage|labels\\(|assignees\\(|files\\(|closingIssuesReferences\\(|timelineItems\\(" scripts src -g '!**/node_modules/**' || trueRepository: TanStack/ai
Length of output: 4588
π Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1,240p' scripts/maintainer/collect.tsRepository: TanStack/ai
Length of output: 7179
π Script executed:
#!/bin/bash
set -euo pipefail
echo '--- types.ts outline ---'
ast-grep outline scripts/maintainer/types.ts --view expanded || true
echo '--- classify.ts outline ---'
ast-grep outline scripts/maintainer/classify.ts --view expanded || true
echo '--- collect.ts relevant remainder ---'
sed -n '240,420p' scripts/maintainer/collect.ts
echo '--- search for files/labels/timeline consumers ---'
rg -n "\.files\b|\.labels\b|\.assignees\b|timeline|closingIssuesReferences|changedFiles|isAnswered|upvoteCount|statusCheckRollup" scripts/maintainer -g '!**/*.test.ts' || trueRepository: TanStack/ai
Length of output: 15367
π Script executed:
#!/bin/bash
set -euo pipefail
echo '--- types.ts outline ---'
ast-grep outline scripts/maintainer/types.ts --view expanded || true
echo '--- classify.ts outline ---'
ast-grep outline scripts/maintainer/classify.ts --view expanded || true
echo '--- collect.ts remainder ---'
sed -n '240,420p' scripts/maintainer/collect.ts
echo '--- downstream consumers of snapshot fields ---'
rg -n "\b(files|labels|assignees|timeline|comments|closingIssuesReferences|changedFiles|isDraft|reviewDecision|statusCheckRollup)\b" scripts/maintainer/classify.ts scripts/maintainer/route.ts scripts/maintainer/actions.ts scripts/maintainer/scorecard.ts scripts/maintainer/sweep.ts scripts/maintainer/metrics.ts scripts/maintainer/comments.ts scripts/maintainer/discord.ts scripts/maintainer/types.ts || trueRepository: TanStack/ai
Length of output: 16999
π Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1,220p' scripts/maintainer/classify.ts
echo '---'
sed -n '220,390p' scripts/maintainer/classify.ts
echo '---'
sed -n '1,140p' scripts/maintainer/comments.ts
echo '---'
sed -n '1,220p' scripts/maintainer/route.tsRepository: TanStack/ai
Length of output: 17652
Don't treat capped nested connections as complete snapshots. files, closingIssuesReferences, timelineItems, and discussion comments are all truncated here, but downstream triage uses them directly; that can hide changesets/E2E coverage, miss maintainer replies or marker comments, and undercount linked issues. Carry completeness metadata through the snapshot or suppress automation when a connection is truncated.
π€ Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/maintainer/collect.ts` around lines 125 - 130, Update the snapshot
collection logic around the GraphQL connections files, closingIssuesReferences,
timelineItems, and discussion comments so capped results are not treated as
complete data. Propagate each connectionβs pagination/completeness metadata
through the collected snapshot, and ensure downstream triage automation either
fetches all pages or suppresses decisions when any required connection is
truncated.
| for (const chunk of chunkEmbeds(embeds)) { | ||
| const response = await fetchImpl(webhookUrl, { | ||
| method: 'POST', | ||
| headers: { 'content-type': 'application/json' }, | ||
| body: JSON.stringify({ | ||
| embeds: chunk, | ||
| allowed_mentions: { parse: ['users'] }, | ||
| }), | ||
| }) |
There was a problem hiding this comment.
π©Ί Stability & Availability | π Major | β‘ Quick win
Bound the Discord webhook request.
Without an abort signal or timeout, a stalled network request can hold the scheduled workflow until its job timeout. Add a bounded request timeout and cover the failure path.
π€ Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/maintainer/discord.ts` around lines 201 - 209, Bound the webhook
request in the chunkEmbeds loop by adding an AbortController-based timeout to
the fetchImpl call, passing its signal and clearing the timer when the request
settles. Handle timeout or abort failures explicitly so the scheduled workflow
exits through the intended failure path, and add coverage for the
stalled-request case.
π― Functional Correctness | π Major | β‘ Quick win
π§© Analysis chain
π Script executed:
#!/bin/bash
set -euo pipefail
# Locate the reviewed file and relevant Discord payload construction
git ls-files scripts/maintainer/discord.ts
ast-grep outline scripts/maintainer/discord.ts --view expanded || true
# Read the surrounding lines around the reported snippet
sed -n '150,240p' scripts/maintainer/discord.ts
# Find any other Discord webhook/message payload usage in the repo
rg -n --hidden --no-ignore-vcs "allowed_mentions|embeds:|content:" scripts/maintainer scripts || trueRepository: TanStack/ai
Length of output: 4193
π Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the embed construction and mention helper
sed -n '1,170p' scripts/maintainer/discord.ts
# Pull Discord webhook/message docs if available from the public docs page
python3 - <<'PY'
import urllib.request
url = "https://discord.com/developers/docs/resources/webhook#execute-webhook"
try:
with urllib.request.urlopen(url, timeout=20) as r:
data = r.read().decode("utf-8", "replace")
# Print only mention-related snippets
for needle in ["allowed_mentions", "embeds", "content", "mentions", "parse"]:
idx = data.lower().find(needle.lower())
if idx != -1:
start = max(0, idx - 250)
end = min(len(data), idx + 700)
print(f"\n--- {needle} ---\n")
print(data[start:end])
except Exception as e:
print(f"FETCH_ERROR: {e}")
PYRepository: TanStack/ai
Length of output: 5884
π Web query:
Discord webhook allowed_mentions embeds mentions content docs
π‘ Result:
The allowed_mentions object in the Discord webhook payload is a top-level property used to control which mentions (users, roles, or @everyone/@here) will trigger a notification [1][2]. Key technical points regarding allowed_mentions and embeds: Scope: The allowed_mentions object applies to the entire webhook message, including both the message content and any embeds [1]. It must be placed at the top level of the JSON payload; if placed inside an embed object, it will be ignored [1]. Functionality: It determines whether a mention string (e.g., <@123>, <@&456>, @everyone) renders as a functional mention that pings a user or role, or if it renders merely as plain text [2]. Mentions within Embeds: While you can include mention syntax (such as <@ID> or <@&ID>) in the text fields of an embed (like title or description), Discord does not support active pings within embeds [3]. These strings will typically render as text rather than clickable, notifying mentions regardless of the allowed_mentions configuration [3]. Configuration: 1. Parse Array: You can specify an array in the parse field containing "users", "roles", or "everyone" to allow auto-parsing for those categories [2]. 2. Whitelisting: Alternatively, you can use the users or roles arrays to whitelist specific IDs [1][2]. 3. Constraints: You cannot use the parse field and the explicit users/roles arrays in the same payload; doing so will result in a 400 Bad Request error [2]. For messages containing both content and embeds, a single top-level allowed_mentions object governs all mention parsing for the message [1]. Top results: [4][5][6][1][2][3]
Citations:
- 1: https://wisechecker.com/discord-webhook-allowed-mentions-whitelist/
- 2: https://discord-webhook.com/en/blog/discord-webhook-mentions/
- 3: @mention roles via discord webhookΒ containrrr/shoutrrr#295
- 4: https://docs.discord.com/developers/resources/webhook
- 5: https://docs.discord.com/developers/resources/message
- 6: https://github.com/discord/discord-api-docs/blob/8e844425/docs/resources/webhook.mdx
π Web query:
site:discord.com/developers/docs resources webhook allowed_mentions embeds content mention in embed no ping
π‘ Result:
To mention a user in a Discord webhook message without triggering a notification (ping), you should use the allowed_mentions object in your webhook payload. By setting the parse array to exclude users or roles, you can prevent the automatic pinging behavior that usually accompanies mentions. The allowed_mentions object allows you to control which mentions are parsed and result in a notification. To prevent all pings, you can use an empty parse array or explicitly exclude specific mention types. Example of an allowed_mentions payload structure: { "content": "Hello <@123456789012345678>", "allowed_mentions": { "parse": [] } } When sending content in an embed, the same allowed_mentions rules apply. Discord parses mentions within embed fields (such as title, description, and field values) based on the configuration provided in the allowed_mentions object. If you do not want the mention in the embed to ping the user, ensure that the allowed_mentions block is included in your request and that it does not explicitly allow user or role mentions (or keeps the parse array empty). Keep in mind that if you include a user ID in the mention format <@USER_ID> or <@!USER_ID> within your content or embed, Discord will render it as a mention. Using the empty parse array in allowed_mentions ensures that these mentions are treated as text only, preventing the associated notification.
Citations:
- 1: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html
- 2: https://discord.com/developers/docs/social-sdk/release_notes.html
- 3: https://discord.com/developers/docs/slayer-sdk/classdiscordpp_1_1MessageHandle.html
- 4: https://discord.com/developers/docs/social-sdk/namespacediscordpp.html
- 5: https://discord.com/developers/docs/slayer-sdk/classdiscordpp_1_1AdditionalContent.html
π Script executed:
#!/bin/bash
set -euo pipefail
# Show the embed builder and mention helper in full
cat -n scripts/maintainer/discord.ts | sed -n '1,170p'Repository: TanStack/ai
Length of output: 6927
Move maintainer mentions into top-level content. The mention() helper currently renders <@...> inside an embed description, and this webhook sends only embeds, so those maintainer pings wonβt notify anyone. Put the mentions in content for the message that should alert maintainers, and whitelist just those IDs in allowed_mentions.users.
π€ Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/maintainer/discord.ts` around lines 201 - 209, Update the webhook
payload in the embed-sending loop to place maintainer mentions in the top-level
content field rather than only inside embed descriptions. Use the existing
mention-related symbols to build that content for alerting messages, and set
allowed_mentions.users to exactly the corresponding maintainer IDs while
preserving the existing embed chunking and POST behavior.
β¦ merge-conflicts label - Workflows: grant issues/pull-requests/discussions read scopes the GraphQL snapshot needs (scorecard had only contents: read) - classify: readyToMerge requires explicit MERGEABLE + non-pending CI; ack/repro markers only count in bot comments; freshContributorReply now requires a maintainer response to exist (and is computed for issues too) - metrics: fresh replies use the real transition for issues, closed response samples exclude roster authors, suspected-spam PRs leave the assignment denominator - config: repo must be exactly one owner/name pair - github/discord: per-request abort timeouts - actions: 1s pacing between mutations, one 60s retry on 403/429, and per-mutation progress output (a silent loop tripped a run watchdog) - new managed merge-conflicts label on conflicting PRs, reconciled like the rest - discord: flagged items lead the New embed with their own count
π― Changes
Adds a maintainer toolset β two scheduled workflows backed by a unit-tested TypeScript toolkit in
scripts/maintainer/, configured via.github/maintainers.json:maintainer-sweep.yml, every 3h): assigns open PRs/issues to maintainers by least-loaded rotation with caps, posts a one-time ack comment with a pre-review checklist (CI, conflicts, changeset, E2E), asks for a reproduction on bug reports lacking one, and keepswaiting-on/ready-to-merge/needs-repro/has-prlabels in sync. Suspected drive-by/bounty PRs are flagged for human judgment, never acted on.maintainer-scorecard.yml): posts a to-do digest β SLA breaches, ready-to-merge PRs, per-maintainer queues, new/stale items, unanswered discussions, and response-time stats. Untrusted item titles are markdown-escaped before rendering.Everything is deterministic and stateless: metrics are recomputed from GitHub timelines each run, PR code is never checked out or executed (no
pull_request_target), and workflows run with minimal permissions and pinned action SHAs. Both workflows support manual dry-run dispatch.AI-assisted triage (issue summaries, smarter repro detection, duplicate detection) is intentionally not in this PR β it will come as a follow-up layered on top of this deterministic base.
Before enabling: create the
DISCORD_MAINTAINER_WEBHOOKsecret and add Discord user IDs tomaintainers.json. Seescripts/maintainer/README.md.β Checklist
pnpm run test:pr.π Release Impact
π€ Generated with Claude Code
Summary by CodeRabbit