feat(agent): auto-match PRs to open GitHub Projects v2 in suggest-mode (#3184)#3269
Conversation
#3184) Adds GitHubProjectsAdapter implementing the ProjectTrackerAdapter interface from #3183, using this codebase's first-ever GraphQL calls (repositoryOwner/projectsV2 for listing with cursor pagination, addProjectV2ItemById for attach) alongside a resolveProjectV2Fields helper for #3185's future status-setting. Confirmed during implementation, not just the community-reported gap noted in the issue: GitHub Apps cannot access Projects v2 owned by a personal User account at all -- only Organization-owned boards. All three JSONbored gate repos are user-owned, so the org-level Projects permission already granted to the App is inert for them specifically (still correct for org-owned self-hosters). listOpenProjects handles this with a single query using an inline fragment on Organization, so a User-owned repo degrades to an empty list, never an error. Generalizes #3183's matchOpenMilestones -> matchOpenTrackerItems and maybeSuggestMilestoneMatch -> maybeSuggestProjectOrMilestoneMatch, which now checks milestones and projects independently and mentions whichever matched in one suggestion comment. The webhook-facing maybeSuggestMilestoneMatchForPr entry point (and processors.ts's call site) is unchanged.
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
|
Tip 🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩 ✅ Gittensory review result - approve/merge recommendedReview updated: 2026-07-05 00:31:22 UTC
✅ Suggested Action - Approve/Merge
Review summary Nits — 5 non-blocking
Review context
Contributor next steps
Signal definitions
🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3269 +/- ##
=======================================
Coverage 94.12% 94.13%
=======================================
Files 276 276
Lines 30202 30234 +32
Branches 11011 11016 +5
=======================================
+ Hits 28429 28461 +32
Misses 1127 1127
Partials 646 646
🚀 New features to boost your workflow:
|
listOpenProjects only queried id/title from the Projects v2 GraphQL API, so a CLOSED board could still be suggested to a PR whose title matched it -- violating the ProjectTrackerAdapter contract that the returned list is open-only. Add closed to the queried fields and filter it out before returning, and fold the previously-unused ListOpenProjectsGraphQlResponse type into an accurate match of the real response shape (pageInfo + closed) instead of leaving a duplicate inline type beside it. Also make maybeSuggestProjectOrMilestoneMatch degrade each tracker lookup independently to an empty list on failure. A single Promise.all meant a transient GraphQL error fetching Projects v2 could throw away an otherwise-valid milestone match, and vice versa, contradicting this function's own doc comment that milestones and projects are checked independently.
Summary
GitHubProjectsAdapterimplementing theProjectTrackerAdapterinterface from feat(agent): auto-match PRs to open GitHub Milestones (suggest-mode) #3183, using this codebase's first-ever GraphQL calls —repositoryOwner/projectsV2for listing (with cursor pagination),addProjectV2ItemByIdfor attach, and aresolveProjectV2Fieldshelper for feat(agent): promote project/milestone matching to auto-apply mode #3185's future status-setting (not yet called — no field value to set until a caller provides one).listOpenProjectshandles this in one query using an inline fragment onOrganization, so a User-owned repo's owner resolves to__typename: "User"and degrades to an empty list — never a thrown error. Issue feat(agent): auto-match PRs to open GitHub Projects v2 (suggest-mode) #3184 updated with the full research and citations.matchOpenMilestones→matchOpenTrackerItems(same tokenize/termOverlap logic, now generic over milestones OR projects) andmaybeSuggestMilestoneMatch→maybeSuggestProjectOrMilestoneMatch, which checks milestones and projects independently (in parallel) and mentions whichever matched (either, both, or neither) in one suggestion comment. The webhook-facingmaybeSuggestMilestoneMatchForPrentry point — and its one call site insrc/queue/processors.ts— is unchanged; this PR touches onlysrc/integrations/project-tracker-adapter.tsand its test file.Scope
type(scope): short summaryConventional Commit format, for examplefix(api): restore profile access checks.CONTRIBUTING.mdand does not reintroduce GitHub Pages, VitePress,site/, orCNAME.Validation
git diff --checknpm run actionlintnpm run typechecknpm run test:coveragelocally;codecov/patchrequires ≥99% coverage of the lines AND branches you changed (aim for 100% on your diff so CI variance does not fail near the threshold). Global coverage is a non-blocking trend with a loose 90% backstop, not the gate.npm run test:workersnpm run build:mcpnpm run test:mcp-packnpm run ui:openapi:checknpm run ui:lintnpm run ui:typechecknpm run ui:buildnpm audit --audit-level=moderateIf any required check was skipped, explain why:
typecheck,git diff --check,db:migrations:check(no new migration — reuses feat(agent): auto-match PRs to open GitHub Milestones (suggest-mode) #3183'sautoProjectMilestoneMatchsetting),ui:openapi:check(no schema change), and every affected suite directly (test/unit/project-tracker-adapter.test.ts— 36 tests, 100% statement/branch/function/line coverage locally on the file) plusnpm run test:changed(57 files / 1611 tests, all green). This PR touches only 2 files (the adapter + its test), neither UI/MCP/workers, so those checks are out of scope. Also ran an independent adversarial verification pass specifically cross-checking the GraphQL query/mutation shapes against GitHub's public GraphQL schema (Organization.projectsV2, Mutation.addProjectV2ItemById, ProjectV2 fields) and confirmingoctokit.graphql()is a real, always-present method on@octokit/core(not a plugin that needs installing) by reading its actual source innode_modules. CI runs the full gate on push.Safety
autoProjectMilestoneMatchOpenAPI schema.)UI Evidencesection below... (N/A — backend-only change, no visible UI.)UI Evidence
N/A — backend-only change, no visible UI.
Notes
attachToProject'smakeInstallationOctokitcall hard-codes mode"live"(matching feat(agent): auto-match PRs to open GitHub Milestones (suggest-mode) #3183's existing pattern), so once feat(agent): promote project/milestone matching to auto-apply mode #3185 actually calls it, the mutation will bypass this repo's dry-run/pause suppression hook unless that PR threads the resolved action mode through instead of reusing the literal.