feat(relations): deterministic PR/issue classification via closing edges - #21
Conversation
fetch closingIssuesReferences per PR in the scan, persist closesIssues in item metadata, and label every cluster with a deterministic relation (pr-issue-linked/-unlinked, prs-only, issues-only) plus resolved in-cluster closingEdges. flows into starmap JSON (additive, schema v1), dupes NDJSON, cluster detail, and the report. relation is omitted for clusters holding pre-upgrade rows: absent means unknown, never "closes nothing". scan now refreshes metadata for unchanged items without re-embedding, so ciStatus/reviewCount/labels/closesIssues stay current on every scan and existing dbs pick up closing refs with one re-scan. closes #20
…fresh review fixes: cross-repo closingIssuesReferences were mapped by bare number, aliasing another repo's issue onto local numbering and risking fabricated closing edges - the query now carries repository.nameWithOwner and the mapper filters to the scanned repo. metadata refresh for unchanged items now writes only on drift (a quiet repo costs zero UPDATEs per scan), and closing-ref truncation past the 100-node page is warned instead of silent.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2962ba431b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| bodyLength: item.body.length, | ||
| nodeId: item.nodeId, | ||
| headRefOid: item.headRefOid, | ||
| closesIssues: item.closesIssues, |
There was a problem hiding this comment.
Preserve closing metadata during REST scans
When a database previously populated by GraphQL is scanned with prism scan --rest, REST PRs do not provide closesIssues (or the other deep signals), but this metadata object is still used by the unchanged-item refresh. The refresh therefore replaces known closing edges with an object where closesIssues is omitted, making subsequent clusters report the relation as unknown despite the data having been fetched earlier. Preserve existing deep metadata when the selected fetcher cannot supply it.
Useful? React with 👍 / 👎.
| @@ -174,6 +186,7 @@ export class GitHubClient { | |||
| changedFiles | |||
There was a problem hiding this comment.
Mark truncated closing references as unknown
For a PR with more than 100 closing references, this query persists only the first page as though it were complete. Although the warning below notes truncation, classifyClusterRelation receives a defined array and can emit pr-issue-unlinked when the relevant in-cluster issue is on a later page. Paginate the connection or retain an unknown state whenever totalCount exceeds the fetched nodes.
Useful? React with 👍 / 👎.
| refs: { nodes?: Array<{ number: number; repository?: { nameWithOwner: string } }> } | null | undefined, | ||
| repoFull: string, | ||
| ): number[] { | ||
| return (refs?.nodes || []).filter((n) => n.repository?.nameWithOwner === repoFull).map((n) => n.number); |
There was a problem hiding this comment.
Compare repository identities case-insensitively
Repository names supplied through config/--repo are not normalized, while repository.nameWithOwner is returned in GitHub's canonical display casing. If a user supplies a valid casing variant (for example, a lowercased repository name), this exact comparison drops every same-repository reference and stores [], causing linked PR/issue clusters to be classified as unlinked. Normalize both sides before comparing.
Useful? React with 👍 / 👎.
Closes #20
what
clusters now say how their members relate, deterministically from github's closing edges:
closingIssuesReferencesper PR (one field on the existing query, no extra requests) and persistsclosesIssuesin item metadata, filtered to same-repo refs (a cross-repo ref must not alias onto local issue numbers)relation:pr-issue-linked|pr-issue-unlinked|prs-only|issues-only, plus resolved in-clusterclosingEdgesSTARMAP_SCHEMA_VERSIONstays 1),dupes --jsonNDJSON rows,dupes --clusterdetail, and the report's verbose sectionrelationis omitted for clusters holding pre-upgrade rows: absent means unknown, never "closes nothing"why
odysseus triage feedback classified cluster roles by hand ("PR/Issue pair with closing edge" / "no closing edge" / sibling issues) and asked for exactly this. github already knows the edges; we just never fetched them.
validation
"relation":"pr-issue-linked","closingEdges":[{"pr":18,"issue":17}]in both NDJSON and starmap, withcloses: [17]on the PR item onlyfollow-ups noted in #20, out of scope here: PR-stack detection (needs baseRefName), cross-repo edge resolution, complementary-vs-competing heuristics.