diff --git a/backlog/completed/BACK-189 - enhance-backlog-triage-emit-comment-and-closing-pr-relationship-signals.md b/backlog/completed/BACK-189 - enhance-backlog-triage-emit-comment-and-closing-pr-relationship-signals.md new file mode 100644 index 0000000..40c88a6 --- /dev/null +++ b/backlog/completed/BACK-189 - enhance-backlog-triage-emit-comment-and-closing-pr-relationship-signals.md @@ -0,0 +1,31 @@ +--- +id: BACK-189 +title: 'enhance(backlog-triage): emit comment and closing-PR relationship signals' +status: Done +labels: + - documentation + - enhancement +priority: medium +milestone: +created_date: '2026-06-06' +--- +## Description +## Context + +#73 added the snapshot-v2 collector fields needed for richer relationship analysis: per-issue `closing_prs` is present by default and `comments` is available through `--with-comments`. The collector is ready, but `triage-relate.js` still emits only body-based mentions/blocks/depends-on and title duplicate candidates. + +## Desired change + +Teach `triage-relate.js` to use snapshot-v2 fields conservatively. + +Suggested signals: +- comment-based mentions when `comments` is present +- a closing-PR relationship signal when `closing_prs` contains merged PR metadata + +## Acceptance Criteria + +- [x] Comment mention scanning runs only when issue `comments` arrays are present. +- [x] Comment-derived edges carry evidence that identifies comment source separately from issue body evidence. +- [x] Closing-PR edges use `closing_prs` and do not imply an automatic close recommendation by themselves. +- [x] Missing optional fields degrade cleanly without warnings or undefined-field behavior. +- [x] Relationship docs and tests cover both enabled and absent-field paths. diff --git a/backlog/tasks/BACK-73 - enhance-backlog-triage-snapshot-v2-schema-for-closing-prs-comments-and-closed-issue-scan.md b/backlog/completed/BACK-73 - enhance-backlog-triage-snapshot-v2-schema-for-closing-prs-comments-and-closed-issue-scan.md similarity index 99% rename from backlog/tasks/BACK-73 - enhance-backlog-triage-snapshot-v2-schema-for-closing-prs-comments-and-closed-issue-scan.md rename to backlog/completed/BACK-73 - enhance-backlog-triage-snapshot-v2-schema-for-closing-prs-comments-and-closed-issue-scan.md index ffd2706..627ecc8 100644 --- a/backlog/tasks/BACK-73 - enhance-backlog-triage-snapshot-v2-schema-for-closing-prs-comments-and-closed-issue-scan.md +++ b/backlog/completed/BACK-73 - enhance-backlog-triage-snapshot-v2-schema-for-closing-prs-comments-and-closed-issue-scan.md @@ -1,7 +1,7 @@ --- id: BACK-73 title: 'enhance(backlog-triage): snapshot v2 schema for closing PRs, comments, and closed-issue scan' -status: To Do +status: Done labels: - documentation - enhancement diff --git a/backlog/sprints/2026-05-backlog-triage-snapshot-v2.md b/backlog/sprints/2026-05-backlog-triage-snapshot-v2.md index 69944d0..6f9d1d7 100644 --- a/backlog/sprints/2026-05-backlog-triage-snapshot-v2.md +++ b/backlog/sprints/2026-05-backlog-triage-snapshot-v2.md @@ -1,6 +1,6 @@ --- milestone: backlog-triage snapshot v2 -status: active +status: completed started: 2026-05-31 due: TBD objectives: [O4] @@ -15,7 +15,7 @@ Shape and implement #73 so backlog-triage can see closing PR links, optional com ## Plan Start with a short technical split before implementation. The issue is larger than a doc polish item because it touches collection schema and downstream scanners. -- [ ] #73 enhance(backlog-triage): snapshot v2 schema for closing PRs, comments, and closed-issue scan +- [x] #73 enhance(backlog-triage): snapshot v2 schema for closing PRs, comments, and closed-issue scan → PR #191 (merged) ## Running Context - Current `triage-relate` and `triage-stale` explicitly defer PR-merged and duplicate-of-closed signals until snapshot v2 fields exist. @@ -25,3 +25,5 @@ Start with a short technical split before implementation. The issue is larger th ## Progress - 2026-05-31: Open issue set synced locally; #73 mirror created under `backlog/tasks/`. Previous spec-grill dogfood sprint completed after PR #175 landed. +- 2026-06-06: #73 completed via PR #191. Collector v2 now emits explicit `schema_version: 2`; downstream analyzer work was split into #189 (relationships) and #190 (stale/obsolete signals). +- 2026-06-06: Sprint closed. 1/1 tasks completed. diff --git a/backlog/sprints/2026-06-backlog-triage-relationships.md b/backlog/sprints/2026-06-backlog-triage-relationships.md new file mode 100644 index 0000000..188c6a1 --- /dev/null +++ b/backlog/sprints/2026-06-backlog-triage-relationships.md @@ -0,0 +1,26 @@ +--- +milestone: backlog-triage relationships +status: completed +started: 2026-06-06 +due: TBD +objectives: [O4] +component: "triage-grooming" +--- + +# backlog-triage relationships + +## Goal +Use snapshot v2 relationship fields in `triage-relate` without turning advisory relationship evidence into automatic close recommendations. + +## Plan +- [x] #189 enhance(backlog-triage): emit comment and closing-PR relationship signals → PR #192 + +## Running Context +- #73 shipped collector-side v2 fields: `closing_prs` by default, `comments` behind `--with-comments`. +- Keep #189 non-mutating. Relationship edges may influence review priority, but they must not create stale/close actions. +- Optional fields must degrade silently when absent so old snapshots remain readable. + +## Progress +- 2026-06-06: Started #189 after closing the completed #73 snapshot-v2 sprint locally. +- 2026-06-06: #189 implemented in PR #192. `triage-relate` now emits `comment-mentions` and advisory `merged-pr-link` edges from snapshot v2 fields. +- 2026-06-06: Sprint closed. 1/1 tasks completed. diff --git a/backlog/sprints/_context.md b/backlog/sprints/_context.md index e3e76d9..9023783 100644 --- a/backlog/sprints/_context.md +++ b/backlog/sprints/_context.md @@ -13,3 +13,5 @@ - Resolve `progress-sync` metric semantics in `#49` before doing structural refactors in `#50` - `progress-sync` and the bash helpers both parse sprint/task markdown, so contract drift needs explicit coverage - `sync-pull.js --update` refreshes task frontmatter and, for machine-managed issues whose **incoming GitHub body** starts with the `/; const DEFAULT_REPORT_DIR = path.join("backlog", "triage"); -const DEFERRED_RELATIONSHIPS_MARKER = - "_(PR/comment relationship signals deferred — collector fields exist; analyzer rules tracked in #189)_"; +const OPTIONAL_RELATIONSHIPS_MARKER = + "_(comment and closing-PR relationship signals run only when snapshot v2 fields are present)_"; const DEFERRED_OBSOLETE_MARKER = "_(closing-PR-already-merged and duplicate-of-closed signals deferred — collector fields exist; stale rules tracked in #190)_"; @@ -247,6 +247,13 @@ function formatRelationshipEdge(edge, issueIndex) { const left = fromIssue ? issueRef(fromIssue) : `#${edge.from}`; const right = toIssue ? issueRef(toIssue) : `#${edge.to}`; + if (edge.kind === "merged-pr-link") { + const pr = edge.evidence?.pr || {}; + const prLabel = pr.number ? `PR #${pr.number}` : "merged PR"; + const mergedAt = pr.mergedAt ? `; mergedAt ${pr.mergedAt}` : ""; + return `- ${left} merged-pr-link ${prLabel}${mergedAt}`; + } + if (edge.kind === "duplicate-candidate") { const overlap = Array.isArray(edge.evidence?.overlap) && edge.evidence.overlap.length > 0 ? `; overlap: ${edge.evidence.overlap.join(", ")}` @@ -262,19 +269,19 @@ function renderRelationships(relate, issueIndex) { const lines = ["## Relationships"]; if (!relate) { - lines.push("_(no input provided)_", "", DEFERRED_RELATIONSHIPS_MARKER); + lines.push("_(no input provided)_", "", OPTIONAL_RELATIONSHIPS_MARKER); return lines.join("\n"); } if (relate.edges.length === 0) { - lines.push("_(none)_", "", DEFERRED_RELATIONSHIPS_MARKER); + lines.push("_(none)_", "", OPTIONAL_RELATIONSHIPS_MARKER); return lines.join("\n"); } for (const edge of [...relate.edges].sort((left, right) => left.from - right.from || left.to - right.to || left.kind.localeCompare(right.kind))) { lines.push(formatRelationshipEdge(edge, issueIndex)); } - lines.push("", DEFERRED_RELATIONSHIPS_MARKER); + lines.push("", OPTIONAL_RELATIONSHIPS_MARKER); return lines.join("\n"); } @@ -428,7 +435,9 @@ function buildRelationshipCounts(relate) { for (const edge of relate.edges) { counts.set(edge.from, (counts.get(edge.from) || 0) + 1); - counts.set(edge.to, (counts.get(edge.to) || 0) + 1); + if (edge.to !== edge.from) { + counts.set(edge.to, (counts.get(edge.to) || 0) + 1); + } } return counts; } @@ -731,7 +740,7 @@ if (require.main === module) main(); module.exports = { ANCHOR_PATTERN, - DEFERRED_RELATIONSHIPS_MARKER, + OPTIONAL_RELATIONSHIPS_MARKER, DEFERRED_OBSOLETE_MARKER, usage, parseArgs, diff --git a/skills/backlog-triage/scripts/triage-report.test.js b/skills/backlog-triage/scripts/triage-report.test.js index 8f2cf29..511e472 100644 --- a/skills/backlog-triage/scripts/triage-report.test.js +++ b/skills/backlog-triage/scripts/triage-report.test.js @@ -26,6 +26,21 @@ function makeSnapshot() { number: 101, title: "OAuth token refresh flow", body: "Blocks #102. See #105 for docs follow-up.", + comments: [ + { + author: "octocat", + body: "Comment follow-up in #103.", + createdAt: "2026-04-18T01:00:00.000Z", + }, + ], + closing_prs: [ + { + number: 88, + state: "MERGED", + mergedAt: "2026-04-18T01:15:00.000Z", + url: "https://github.com/sungjunlee/dev-backlog/pull/88", + }, + ], labels: ["type:feature", "priority:medium"], createdAt: "2026-04-10T01:30:00.000Z", updatedAt: "2026-04-17T01:30:00.000Z", @@ -356,7 +371,9 @@ describe("triage-report integration chain", () => { assert.match(markdown, //); assert.match(markdown, //); assert.match(markdown, //); - assert.match(markdown, /PR\/comment relationship signals deferred/); + assert.match(markdown, /#101 OAuth token refresh flow comment-mentions #103 Audit token rotation docs/); + assert.match(markdown, /#101 OAuth token refresh flow merged-pr-link PR #88; mergedAt 2026-04-18T01:15:00.000Z/); + assert.match(markdown, /comment and closing-PR relationship signals run only when snapshot v2 fields are present/); assert.match(markdown, /closing-PR-already-merged and duplicate-of-closed signals deferred/); // Classification groups must match Done Criteria: theme / label / age.