Skip to content

feat(agent): auto-match PRs to open GitHub Projects v2 in suggest-mode (#3184)#3269

Merged
loopover-orb[bot] merged 2 commits into
mainfrom
feat/auto-project-match
Jul 5, 2026
Merged

feat(agent): auto-match PRs to open GitHub Projects v2 in suggest-mode (#3184)#3269
loopover-orb[bot] merged 2 commits into
mainfrom
feat/auto-project-match

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Summary

  • Adds GitHubProjectsAdapter implementing the ProjectTrackerAdapter interface from feat(agent): auto-match PRs to open GitHub Milestones (suggest-mode) #3183, using this codebase's first-ever GraphQL callsrepositoryOwner/projectsV2 for listing (with cursor pagination), addProjectV2ItemById for attach, and a resolveProjectV2Fields helper 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).
  • Correction found during implementation, not just the community-reported gap the issue flagged: GitHub Apps cannot access Projects v2 owned by a personal User account at all — only Organization-owned boards. All three JSONbored gate repos (gittensory, metagraphed, awesome-claude) are user-owned, so the org-level Projects permission already granted to the App is inert for them specifically (harmless to leave — and it's exactly correct for any other self-hoster running gittensory under a GitHub Organization). listOpenProjects handles this in one query using an inline fragment on Organization, 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.
  • Generalizes feat(agent): auto-match PRs to open GitHub Milestones (suggest-mode) #3183's matchOpenMilestonesmatchOpenTrackerItems (same tokenize/termOverlap logic, now generic over milestones OR projects) and maybeSuggestMilestoneMatchmaybeSuggestProjectOrMilestoneMatch, which checks milestones and projects independently (in parallel) and mentions whichever matched (either, both, or neither) in one suggestion comment. The webhook-facing maybeSuggestMilestoneMatchForPr entry point — and its one call site in src/queue/processors.ts — is unchanged; this PR touches only src/integrations/project-tracker-adapter.ts and its test file.
  • Closes feat(agent): auto-match PRs to open GitHub Projects v2 (suggest-mode) #3184.

Scope

  • The PR title follows type(scope): short summary Conventional Commit format, for example fix(api): restore profile access checks.
  • This PR is focused and does not mix unrelated backend, UI, MCP, docs, dependency, and deploy changes.
  • This follows CONTRIBUTING.md and does not reintroduce GitHub Pages, VitePress, site/, or CNAME.
  • I linked an issue, or this is small enough that the summary explains why an issue is not needed.

Validation

  • git diff --check
  • npm run actionlint
  • npm run typecheck
  • npm run test:coverage locally; codecov/patch requires ≥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:workers
  • npm run build:mcp
  • npm run test:mcp-pack
  • npm run ui:openapi:check
  • npm run ui:lint
  • npm run ui:typecheck
  • npm run ui:build
  • npm audit --audit-level=moderate
  • New or changed behavior has unit/integration tests for new branches, fallback paths, and sanitizer boundaries

If any required check was skipped, explain why:

  • Ran targeted validation instead of the full local gate: typecheck, git diff --check, db:migrations:check (no new migration — reuses feat(agent): auto-match PRs to open GitHub Milestones (suggest-mode) #3183's autoProjectMilestoneMatch setting), 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) plus npm 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 confirming octokit.graphql() is a real, always-present method on @octokit/core (not a plugin that needs installing) by reading its actual source in node_modules. CI runs the full gate on push.

Safety

  • No secrets, wallet details, hotkeys, coldkeys, user PATs, private keys, raw trust scores, private rankings, or private maintainer evidence are exposed.
  • Public GitHub text stays sanitized, low-noise, and does not imply compensation guarantees or optimization tactics.
  • Auth, cookie, CORS, GitHub App, Cloudflare, or session changes include negative-path tests. (N/A — no auth/CORS/session surface touched; covered by fetch-stub tests including invalid-repo-name, User-owner-empty-result, and GraphQL-item-null paths.)
  • API/OpenAPI/MCP behavior is updated and tested where needed. (N/A — no new config field or API surface; reuses feat(agent): auto-match PRs to open GitHub Milestones (suggest-mode) #3183's existing autoProjectMilestoneMatch OpenAPI schema.)
  • UI changes use live API data or real empty/error/loading states, not production mock/demo fallbacks. (N/A — no UI changes.)
  • Visible UI changes include a UI Evidence section below... (N/A — backend-only change, no visible UI.)
  • Public docs/changelogs are updated where needed... (N/A — no changelog edit per house rule.)

UI Evidence

N/A — backend-only change, no visible UI.

Notes

#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-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@loopover-orb loopover-orb Bot added the gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. label Jul 5, 2026
@loopover-orb

loopover-orb Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Tip

🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩

✅ Gittensory review result - approve/merge recommended

Review updated: 2026-07-05 00:31:22 UTC

2 files · 1 AI reviewer · no blockers · readiness 100/100 · CI green · clean

✅ Suggested Action - Approve/Merge

  • safe to merge

Review summary
The change cleanly adds a GitHub Projects v2 adapter alongside the existing milestone adapter, keeps suggest-mode non-mutating, and preserves the webhook-facing entry point while broadening matching to both tracker types. The most important wiring is covered: user-owned repo degradation, org project pagination, closed-project filtering, independent fail-open lookups, and idempotent comment detection all have focused tests. I do not see a reachable correctness blocker in the provided diff.

Nits — 5 non-blocking
  • nit: src/integrations/project-tracker-adapter.ts:323 now mints one installation token for milestones, another for projects, and a third for comment scanning/posting on the hot suggest path; consider threading a shared octokit/token through the two GitHub adapters if this path starts hitting rate or latency pressure.
  • nit: src/integrations/project-tracker-adapter.ts:287 keeps the exported marker name generic but the marker payload is still `gittensory-milestone-suggest:v1`; that is compatible for idempotency, but add a short comment there so a future cleanup does not accidentally break duplicate suppression for old comments.
  • src/integrations/project-tracker-adapter.ts:323: consider sharing the installation octokit across the milestone lookup, project lookup, and comment scan to avoid three token creations for one advisory suggestion.
  • src/integrations/project-tracker-adapter.ts:287: document that the old milestone marker value is intentionally retained for backwards-compatible idempotency.
  • PR author also opened the linked issue — Link an issue that was opened by a different contributor, or provide a rationale for why this self-authored issue represents genuine discovery work.
Signal Result Evidence
Code review ✅ No blockers 1 reviewer
Linked issue ✅ Linked #3184
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low review scope from cached public metadata (1 linked issue).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 56 registered-repo PR(s), 46 merged, 429 issue(s).
Contributor context ✅ Confirmed Gittensor contributor JSONbored; Gittensor profile; 56 PR(s), 429 issue(s).
Gate result ✅ Passing No configured blocker found.
Review context
  • Author: JSONbored
  • Role context: owner (maintainer lane)
  • Public audience mode: oss maintainer
  • Lane context: Repository registration is not available in the local Gittensory cache.
  • Public profile languages: Python, TypeScript, JavaScript, Ruby, Go, Kotlin, MDX, Shell
  • Official Gittensor activity: 56 PR(s), 429 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Treat this as maintainer-lane context rather than normal contributor-lane activity.
  • No action.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.

🟩 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.

  • Re-run Gittensory review

@codecov

codecov Bot commented Jul 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.13%. Comparing base (9b8d05a) to head (79be05c).
⚠️ Report is 8 commits behind head on main.
✅ All tests successful. No failed tests found.

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           
Files with missing lines Coverage Δ
src/integrations/project-tracker-adapter.ts 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@loopover-orb loopover-orb Bot added the manual-review Gittensor contributor context label Jul 5, 2026
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.

@loopover-orb loopover-orb Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Gittensory approves — the gate is satisfied and CI is green.

@loopover-orb
loopover-orb Bot merged commit 45c47e4 into main Jul 5, 2026
10 checks passed
@loopover-orb
loopover-orb Bot deleted the feat/auto-project-match branch July 5, 2026 00:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. manual-review Gittensor contributor context

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(agent): auto-match PRs to open GitHub Projects v2 (suggest-mode)

1 participant