feat: /assign self-assignment for good first issue (#62)#64
feat: /assign self-assignment for good first issue (#62)#64rtibblesbot wants to merge 6 commits into
Conversation
21bfa84 to
d26fee9
Compare
d26fee9 to
f0a9910
Compare
|
Rebased onto |
There was a problem hiding this comment.
This PR shouldn't affect unrelated files
There was a problem hiding this comment.
Restored - this was deleted accidentally.
|
I was unable to complete this task after multiple attempts. Manual review and intervention may be needed. |
4 similar comments
|
I was unable to complete this task after multiple attempts. Manual review and intervention may be needed. |
|
I was unable to complete this task after multiple attempts. Manual review and intervention may be needed. |
|
I was unable to complete this task after multiple attempts. Manual review and intervention may be needed. |
|
I was unable to complete this task after multiple attempts. Manual review and intervention may be needed. |
0162e75 to
5d1e856
Compare
There was a problem hiding this comment.
There is no need to test constants. The file can be removed.
| const ISSUE_LABEL_HELP_WANTED = 'help wanted'; | ||
| const ISSUE_LABEL_GOOD_FIRST_ISSUE = 'good first issue'; | ||
| const MAX_ASSIGNED_ISSUES = 2; | ||
| const COOLDOWN_DAYS = 7; |
There was a problem hiding this comment.
| const COOLDOWN_DAYS = 7; | |
| const ASSIGN_COOLDOWN_DAYS = 7; |
There was a problem hiding this comment.
Renamed to ASSIGN_COOLDOWN_DAYS throughout.
| | **Other** | `help-wanted` assigned to commenter | Regular/keyword | **Yes** | No | No | - | | ||
| | **Other** | `help-wanted` assigned to someone else | Regular | No | Yes | No | - | | ||
| | **Other** | `help-wanted` assigned to someone else | Assignment request | No | Yes | Yes`*` | `BOT_MESSAGE_ALREADY_ASSIGNED` | | ||
| | Any`**` | Private | `/assign` | No | Yes | Yes | `BOT_MESSAGE_ISSUE_NOT_OPEN` | |
There was a problem hiding this comment.
It'd be better to remove all these new 'Any' table entries and organize the new information to the original table structure - that is utilizing Core team, Close contributor, Issue creator, and Other categories, and none other. This will improve understanding the information a lot.
Furthermore let's use the following language for describing comment type
/assigncommand- assign keyword (links to https://github.com/learningequality/.github/blob/main/scripts/constants.js#L44)
- regular
Finally, let's not use shortcuts, such as GFI - it's not immediately clear to what it refers to.
There was a problem hiding this comment.
Restructured: removed Any** rows, integrated /assign command rows under Close contributor/Issue creator/Other contributor types, updated comment type terminology (regular/assign keyword//assign command), and replaced GFI with good first issue throughout.
There was a problem hiding this comment.
Two items missed in the previous pass: split the combined 'Close contributor, Issue creator, Other' rows into separate rows per contributor type (18 rows total — 6 per category), and linked 'assign keyword' to constants.js#L44 throughout the table.
|
@rtibblesbot we decided not to implement tests for affected GitHub workflows. So all test files, including test setup and changes to package.json and yarn.lock can be removed. |
|
Done. Removed all test files ( |
There was a problem hiding this comment.
There should be no updates to yarn.lock in this PR. Let's revert completely
There was a problem hiding this comment.
Reverted. yarn.lock now matches main exactly.
| es6: true, | ||
| browser: true, | ||
| node: true, | ||
| jest: true, |
8493113 to
5485963
Compare
There was a problem hiding this comment.
Local review against #62 ACs. CI green; no UI files. Findings inline.
Comment on a line not in the diff:
scripts/manage-issue-header.js:62 — suggestion: When help wanted is removed, the header flips to "not open" but the /assign guidance comment stays, inviting /assign that returns BOT_MESSAGE_ISSUE_NOT_OPEN. Add deleteBotComments(issueNumber, LE_BOT_USERNAME, ASSIGN_GUIDANCE_MARKER, { github, context, core }) in the unlabeled branch (needs a utils/constants import).
| good-first-issue-comment: | ||
| name: Post good first issue guidance comment | ||
| if: >- | ||
| github.event.action == 'labeled' && github.event.label.name == 'good first issue' |
There was a problem hiding this comment.
blocking: This fires only on the good first issue labeled event, and good-first-issue-comment.js:27 requires help wanted already present. Tagging GFI before help wanted is valid — a good first issue not yet open. The normal prepare-then-open flow adds help wanted second, so nothing re-runs this check and guidance never posts on issues that just opened. Also trigger on the help wanted labeled event and require both labels in the script before posting; a lone GFI stays silent.
There was a problem hiding this comment.
Fixed in 4422dc8. Now triggers on both good first issue labeled and help wanted labeled events. good-first-issue-comment.js already checks for both labels before posting, so a lone GFI label stays silent — guidance only posts when both are present.
| const repoUnassignments = []; | ||
| try { | ||
| const q = `involves:${username} repo:${owner}/${repo} is:issue updated:>=${since}`; | ||
| const { data } = await github.rest.search.issuesAndPullRequests({ q }); |
There was a problem hiding this comment.
suggestion: search.issuesAndPullRequests({ q }) returns only the first 30 results, so an active contributor's unassignments can be missed and the cooldown under-counts. Use github.paginate(github.rest.search.issuesAndPullRequests, { q, per_page: 100 }) and iterate the result directly (drop data.items).
There was a problem hiding this comment.
Fixed in 4422dc8. Changed to github.paginate(github.rest.search.issuesAndPullRequests, { q, per_page: 100 }) and iterate the result directly (no data.items).
| // This intentionally bypasses shouldContactSupport so | ||
| // /assign activity never reaches #support-dev (only | ||
| // #support-dev-notifications). See docs/community-automations.md. | ||
| const isAssignCommand = commentBody.trim().toLowerCase() === '/assign'; |
There was a problem hiding this comment.
suggestion: Exact match drops /assign me, which falls through to keyword detection instead of assigning. /\/assign\b/i.test(commentBody) accepts /assign me and please /assign while rejecting /assignee.
There was a problem hiding this comment.
Implemented in 4422dc8. Changed to /\/assign\b/i.test(commentBody) — accepts /assign me and please /assign while rejecting /assignee. The plan had exact-match but the regex is clearly better UX.
| @@ -1,3 +1,2 @@ | |||
| github: learningequality | |||
| custom: ["https://learningequality.org/donate/", "https://www.every.org/learningequality"] | |||
There was a problem hiding this comment.
suggestion: Unrelated trailing-newline strip (accidental reformat in 0d2508a). Revert: git checkout main -- .github/FUNDING.yml.
There was a problem hiding this comment.
No change needed — git diff main -- .github/FUNDING.yml shows no diff. The file already matches main (trailing newline is present).
| // Deduplicate by issueUrl, keeping the most recent unassignment event. | ||
| // A user could be assigned/unassigned multiple times on the same issue | ||
| // within the cooldown window, and each event should only count once. | ||
| const byIssue = new Map(); |
There was a problem hiding this comment.
praise: Dedup-by-issue keeping the latest unassignment cleanly handles multiple assign/unassign cycles on one issue.
There was a problem hiding this comment.
Glad that reads clearly.
|
|
||
| // Filter unassignments to exclude currently assigned issues | ||
| const assignedUrls = new Set(assignedIssues.map(i => i.html_url)); | ||
| const filteredUnassignments = recentUnassignments.filter(u => !assignedUrls.has(u.issueUrl)); |
There was a problem hiding this comment.
praise: Filtering cooldown entries against currently-assigned issues prevents double-counting a reassigned issue — correct slot math.
There was a problem hiding this comment.
Glad that reads clearly.
|
Implemented the |
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…lity#62) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…gequality#62) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…uality#62) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…gequality#62) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4422dc8 to
7740ed5
Compare
Summary
Implements the
/assignself-assignment mechanism forgood first issueissues, as agreed in #47.Contributors can comment
/assignon issues labeledhelp wanted+good first issueto self-assign, subject to a 2-issue cross-repo limit and 7-day cooldown on recently dropped issues. The bot posts a guidance comment when thegood first issuelabel is applied, and keyword-based assignment requests on GFI issues now include/assignguidance.All
/assignSlack notifications route to#support-dev-notificationsonly.References
Reviewer guidance
Manual testing is done in test-actions. Temporarily update reusable workflow references to point to this branch.
Key test scenarios:
/assignon an unassignedhelp wanted+good first issueissue → bot assigns and confirms/assignwhen at the 2-issue limit → bot declines with assignment list and cooldown infogood first issuelabel to ahelp wantedissue → bot posts guidance comment/assignguidanceRisky areas:
scripts/utils.js:getRecentUnassignments) — depends on GitHub search indexing latency and timeline event formatscripts/utils.js~line 320) — ensures multiple assign/unassign cycles on the same issue count as one slot/assigndecision tree inhandleAssignCommand(scripts/contributor-issue-comment.js) — many branches with specific Slack routing per pathAI usage
Built entirely by Claude Code following a detailed implementation plan (see
PLAN/index.md). Each task was implemented incrementally and code was reviewed for correctness after each step. Test infrastructure was removed per reviewer feedback (no unit tests needed for this codebase).@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly
How was this generated?