Skip to content

refactor: replace backlog-triage judgment heuristics with prompt guidance (#358) - #359

Merged
sungjunlee merged 6 commits into
mainfrom
refactor/backlog-triage-judgment-to-prompt
Aug 8, 2026
Merged

refactor: replace backlog-triage judgment heuristics with prompt guidance (#358)#359
sungjunlee merged 6 commits into
mainfrom
refactor/backlog-triage-judgment-to-prompt

Conversation

@sungjunlee

@sungjunlee sungjunlee commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Summary

Spike Option B (measure-first, user-approved): replace ~265 judgment/heuristic lines in backlog-triage scripts with prompt-level rubric guidance. Deterministic computation stays in scripts; semantic judgment moves to the model.

Changes

File What changed
scripts/triage-relate.js Removed phrase-based blocks/depends-on (scanPhraseEdges/scanBlocks/scanDependsOn) and title-Jaccard duplicates (tokenizeTitle/jaccardSimilarity/findDuplicateCandidates). Script now emits deterministic edges only: mentions, comment-mentions, merged-pr-link. Dropped unused readTriageConfig/resolveBacklogDir/DEFAULT_CONFIG_PATH.
scripts/triage-stale.js Removed scanDuplicateOfClosed/closedIssueMatches/jaccardSimilarity/tokenizeTitle and SIGNALS.DUPLICATE_OF_CLOSED. Keeps deterministic date/label signals: inactive, wontfix, invalid, merged-closing-pr.
scripts/triage-report.js Removed rule-based buildThemeStats/buildRelationshipCounts/buildPriorityActions/buildMilestoneActions (and getIsoWeek/nextSprintName). Added --model-actions PATH — the model's judged priority/milestone actions are rendered deterministically (anchors, dedupe, Apply Checklist unchanged).
SKILL.md + references/ ×4 --model-actions JSON format, judgment-distribution principle, and rubrics for model-judged blocks/depends-on/duplicate/priority/milestone.
CHANGELOG.md [Unreleased] → Removed entry. Closes #358.

Judgment distribution

  • Scripts (deterministic): issue refs, merged-PR links, dates, labels, anchor rendering, dedupe, Apply Checklist.
  • Model (semantic): blocks, depends-on, duplicate candidates (incl. duplicate-of-closed), priority and milestone proposals — delivered as --model-actions JSON so the report's machine contract never changes.

Verification

  • 453 tests pass / 0 fail; smoke-test 190 pass / 0 fail.
  • Net delta: 197 insertions / 657 deletions across 12 files (~−460 lines).

Summary by CodeRabbit

  • 새 기능

    • 모델 판단 결과를 입력받아 관계, 우선순위, 마일스톤 및 종료 제안을 백로그 보고서에 반영할 수 있습니다.
    • 모델 액션은 검증·중복 제거 후 보고서와 적용 체크리스트에 표시됩니다.
    • 멘션, 댓글 멘션, 병합된 PR 링크 관계는 자동으로 수집됩니다.
  • 개선 사항

    • 관계 및 중복 판단에 이슈 제목, 본문, 라벨, 댓글을 종합한 의미 기반 검토를 적용했습니다.
    • 모델 판단 결과의 근거와 대상이 보고서에 함께 표시됩니다.
    • 관계·우선순위·마일스톤 제안 기준과 JSON 형식을 문서화했습니다.

…ance (#358)

Spike Option B: scripts keep deterministic signals (mentions, merged-PR
links, dates, labels); the model owns semantic judgment (blocks,
depends-on, duplicates, priority and milestone proposals).

- triage-relate.js: drop scanPhraseEdges/scanBlocks/scanDependsOn and
  title-Jaccard duplicates; emits mentions/comment-mentions/merged-pr-link
- triage-stale.js: drop scanDuplicateOfClosed/closedIssueMatches/jaccard;
  keeps inactive/wontfix/invalid/merged-closing-pr
- triage-report.js: drop rule-based priority/milestone builders; add
  --model-actions PATH so model-judged actions render deterministically
- Rubrics: relationships.md, stale.md, decision-review.md, SKILL.md

453 tests pass / 0 fail; smoke-test 190 pass; net -454 script/test lines.
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@sungjunlee, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 37 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: cbc15fb3-3ed7-41e6-b4fa-55f9814dff96

📥 Commits

Reviewing files that changed from the base of the PR and between 4ff2f45 and 57d2d38.

📒 Files selected for processing (2)
  • skills/backlog-triage/scripts/triage-report.js
  • skills/backlog-triage/scripts/triage-report.test.js

Walkthrough

backlog-triage의 의미 기반 휴리스틱을 제거했습니다. 관계·중복·우선순위·마일스톤 판단은 모델 지침과 --model-actions 입력으로 이동했습니다. 스크립트는 결정론적 신호를 생성하고 보고서 렌더링을 수행합니다.

Changes

결정론적 관계 및 stale 신호 축소

Layer / File(s) Summary
결정론적 관계 및 stale 신호 축소
skills/backlog-triage/scripts/triage-relate.js, skills/backlog-triage/scripts/triage-relate.test.js, skills/backlog-triage/scripts/triage-stale.js, skills/backlog-triage/scripts/triage-stale.test.js, skills/backlog-triage/references/relationships.md, skills/backlog-triage/references/stale.md, skills/backlog-triage/references/classification.md
blocks, depends-on, 제목 유사도 기반 중복 탐지를 제거했습니다. 관계 분석은 멘션, 댓글 멘션, 병합 PR 링크만 생성합니다. 종료 이슈 중복 신호와 전용 처리를 제거했습니다.

모델 액션 기반 보고서 생성

Layer / File(s) Summary
모델 액션 기반 보고서 생성
skills/backlog-triage/scripts/triage-report.js, skills/backlog-triage/scripts/triage-report.test.js
--model-actions PATH--model-actions=PATH 입력을 추가했습니다. JSON 배열과 액션 필드를 검증하고 정규화합니다. 모델 액션을 관계·폐기·우선순위·마일스톤 섹션에 반영합니다. 앵커, 중복 제거, Apply Checklist 렌더링을 유지합니다.

모델 액션 규칙 및 실행 연결

Layer / File(s) Summary
모델 액션 규칙 및 실행 연결
skills/backlog-triage/SKILL.md, skills/backlog-triage/references/decision-review.md, README.md, skills/dev-backlog/references/workflow-patterns.md, CHANGELOG.md, skills/dev-backlog/scripts/lib.js, skills/dev-backlog/scripts/lib.test.js
모델 액션 JSON 형식과 우선순위·마일스톤 규칙을 문서화했습니다. 실행 예시에 모델 액션 파일을 추가했습니다. duplicate_threshold 설정과 관련 검증을 제거했습니다.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant ModelActions
  participant TriageReport
  participant DeterministicSignals
  CLI->>TriageReport: --model-actions PATH
  TriageReport->>ModelActions: JSON 배열 로드
  ModelActions-->>TriageReport: 검증된 모델 액션
  TriageReport->>DeterministicSignals: 관계·stale 신호 병합
  TriageReport-->>CLI: triage 보고서와 Apply Checklist
Loading

Possibly related PRs

Poem

모델 액션을 등에 지고,
토끼가 보고서를 달리네.
멘션은 또렷이 남고,
제안은 검증되어 도착하네.
앵커와 체크리스트도 깡충!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 backlog-triage의 판단 휴리스틱을 프롬프트 지침으로 대체하는 주요 변경을 정확하고 간결하게 설명합니다.
Linked Issues check ✅ Passed [#358]의 의미 판단 모델 이전, 결정론적 처리 유지, model-actions 검증·렌더링, 문서화 요구사항을 모두 반영합니다.
Out of Scope Changes check ✅ Passed 변경 사항은 #358의 모델 판단 이전, 설정 제거, 문서화, 테스트 및 관련 워크플로 업데이트 범위에 포함됩니다.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/backlog-triage-judgment-to-prompt

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d0b12f883d

ℹ️ 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".

Comment on lines 568 to 569
const sections = [
{ key: "classification", title: "Classification", markdown: renderClassification(snapshot) },

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Route model edges into Relationships

When --model-actions contains a model-judged blocks, depends-on, or duplicate-candidate edge, this renderer still passes only the deterministic relate result to renderRelationships; loadModelActions also discards the edge's from, to, kind, and confidence fields. Consequently every semantic relationship removed from triage-relate.js is absent from the report, despite the new workflow documenting --model-actions as its replacement transport. Preserve and merge model edges into the relationship input before rendering.

AGENTS.md reference: AGENTS.md:L46-L46

Useful? React with 👍 / 👎.

Comment on lines +563 to +566
const obsoleteActions = buildObsoleteActions(stale, { protectedIssueNumbers });
const priorityActions = buildPriorityActions(snapshot, relate, obsoleteActions);
const milestoneActions = buildMilestoneActions(snapshot, relate, obsoleteActions, priorityActions);
const allActions = [...obsoleteActions, ...priorityActions, ...milestoneActions];
const priorityActions = modelActions.filter((action) => action.section === "priority");
const milestoneActions = modelActions.filter((action) => action.section === "milestone");
const allActions = dedupeActions([...obsoleteActions, ...priorityActions, ...milestoneActions]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Include model-judged duplicate closures in obsolete actions

When the model identifies an open issue as a duplicate of a closed issue, the advertised replacement flow cannot surface that proposal: obsoleteActions is built solely from deterministic stale output, while model actions are accepted only for priority and milestone. An obsolete/close-duplicate model action is silently dropped from both Obsolete Candidates and the Apply Checklist, so removing scanDuplicateOfClosed eliminates this capability rather than transferring its judgment to the model. Merge validated obsolete model actions here while retaining active-sprint protection.

AGENTS.md reference: AGENTS.md:L46-L46

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@skills/backlog-triage/scripts/triage-report.js`:
- Around line 598-607: Validate model actions before the mapping in the triage
report flow, enforcing allowed section/verb combinations, positive safe-integer
issueNumber values, plain-object args, and each section’s required arguments.
Reject unsupported or malformed actions with an error before rendering or
including them in priorityActions and the Apply Checklist, and add a CLI test
covering an invalid model action file.
- Around line 560-566: Model relationship edges and duplicate-of-closed obsolete
proposals are currently dropped from the report. In
skills/backlog-triage/scripts/triage-report.js:560-566, define and load separate
model-action schemas through loadModelActions and merge relationship actions
into the existing Relationships path and obsolete proposals into Obsolete
Candidates. Document the supported edge and proposal formats in
skills/backlog-triage/references/relationships.md:60-89 and
skills/backlog-triage/references/stale.md:18-36, limit claims to implemented
behavior in skills/backlog-triage/SKILL.md:71-79, add JSON and markdown contract
coverage in skills/backlog-triage/scripts/triage-report.test.js:376-419, and
align the completion wording in CHANGELOG.md:11.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d7400dac-1143-4f77-8919-1c4ad241a7f9

📥 Commits

Reviewing files that changed from the base of the PR and between a356a23 and d0b12f8.

📒 Files selected for processing (12)
  • CHANGELOG.md
  • skills/backlog-triage/SKILL.md
  • skills/backlog-triage/references/classification.md
  • skills/backlog-triage/references/decision-review.md
  • skills/backlog-triage/references/relationships.md
  • skills/backlog-triage/references/stale.md
  • skills/backlog-triage/scripts/triage-relate.js
  • skills/backlog-triage/scripts/triage-relate.test.js
  • skills/backlog-triage/scripts/triage-report.js
  • skills/backlog-triage/scripts/triage-report.test.js
  • skills/backlog-triage/scripts/triage-stale.js
  • skills/backlog-triage/scripts/triage-stale.test.js
💤 Files with no reviewable changes (1)
  • skills/backlog-triage/scripts/triage-stale.js

Comment thread skills/backlog-triage/scripts/triage-report.js
Comment thread skills/backlog-triage/scripts/triage-report.js Outdated
Address review feedback on PR #359 (claude-opus-5 + codex gpt-5.6-sol, both NOT-LGTM):

- loadModelActions now validates every entry: allowed section/verb combos,
  positive safe-integer issueNumber (or from/to for relationship edges),
  plain-object args, per-section required args, non-empty summary; malformed
  entries throw with the array index
- --model-actions gains section "relationship" (edges merge into
  Relationships) and section "obsolete" (close/revisit/close-duplicate
  merge into Obsolete Candidates), so the docs' claims now match behavior
  and merge-into has a producer again
- dedupeActions keys match the apply contract (verb, issueNumber, args)
  instead of section:verb:issueNumber
- priority/milestone sections render evidence; renderObsoleteCandidates
  shows model actions even without a stale input
- docs: SKILL.md phase model + JSON examples, README/workflow-patterns
  recipes, CHANGELOG line count; drop now-inert duplicate_threshold
  (lib.js, triage-config.yml, collect/lib tests)
- tests: loadModelActions file loading + 7 rejection cases, relationship/
  obsolete merge, apply-contract dedupe

464 tests pass / 0 fail; smoke-test 190 pass.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e17c01886e

ℹ️ 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".

}
}

const requiredArgs = section === "priority" ? ["value"] : section === "milestone" ? ["name"] : [];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Require a target for close-duplicate actions

When a model-generated close-duplicate action omits args.target or supplies an empty value, this validator accepts it because obsolete actions have no required payload fields. If the proposal is checked, triage-apply.js posts a malformed Duplicate of . comment and then invokes gh issue close on the issue, leaving a destructively closed issue without its duplicate target; reject such actions before rendering.

Useful? React with 👍 / 👎.

Comment on lines +675 to +679
} else {
if (!Number.isSafeInteger(action.issueNumber) || action.issueNumber <= 0) {
throw new Error(`${label} must carry a positive safe-integer issueNumber.`);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Reject mutation actions outside the open-issue snapshot

When the model hallucinates an issue number or swaps the open duplicate with a closed target, any positive integer passes this check. The resulting accepted anchor can mutate or close an unrelated issue in the repository, whereas the removed builders could only create actions while iterating snapshot.issues; cross-check non-relationship issueNumber values against the snapshot before rendering actionable anchors.

Useful? React with 👍 / 👎.

issueNumber: action.section === "relationship" ? undefined : action.issueNumber,
args: action.args,
cluster: typeof action.cluster === "string" ? action.cluster : undefined,
sprintName: typeof action.sprintName === "string" ? action.sprintName : undefined,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Derive the milestone group from the required milestone name

A milestone action with the required args.name but no top-level sprintName passes validation and is normalized here with sprintName: undefined. renderMilestoneSuggestions groups on that field, so otherwise valid model output renders under a literal ### undefined heading; require sprintName or default it from args.name.

Useful? React with 👍 / 👎.

Round-2 review (claude-opus-5 + codex gpt-5.6-sol, both NOT-LGTM):

- validateModelAction now rejects null/empty-string required args
  (priority->value, milestone->name), requires reason for obsolete close
  and target+reason for close-duplicate, and requires a top-level
  sprintName for milestone grouping (prevents '### undefined' headings and
  a localeCompare crash that would abort the whole report)
- dedupe key now uses the same normalizeArgs/stableSerialize as
  triage-apply.js (deferred require to avoid the circular dependency),
  so key order and whitespace no longer cause duplicate anchors
- references/relationships.md and stale.md now document the wire shape
  the loader actually accepts (section/verb/args wrapper) instead of raw
  edges or the old suggested_action grammar
- drop the inert duplicate_threshold remnants from the report test fixture

467 tests pass / 0 fail; smoke-test 190 pass.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4ff2f4556b

ℹ️ 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".

Comment on lines 357 to 359
for (const action of actions) {
const key = `${action.section}:${action.verb}:${action.issueNumber}`;
const key = normalizeActionKey(action);
const current = selected.get(key);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Deduplicate competing close proposals per issue

When one issue satisfies multiple stale signals—for example, a wontfix issue that is also older than stale_days—each generated close action has different reason args, so this full apply-key dedupe retains every proposal. If the reviewer checks both, triage-apply.js executes two comment-and-close command pairs because its idempotency key also includes the args; this can duplicate comments or fail on the second close and stop later accepted actions. Preserve one winning close proposal per issue/verb while using the full key only to dedupe repeated render surfaces.

Useful? React with 👍 / 👎.

Comment on lines +721 to +723
sprintName: typeof action.sprintName === "string" ? action.sprintName : undefined,
summary: String(action.summary),
evidence: typeof action.evidence === "string" ? action.evidence : undefined,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Derive checkbox text from the mutation payload

When model output supplies a summary that disagrees with its verb or args, validation accepts it and this value becomes the human-facing checkbox text while triage-apply.js executes the anchor payload. For example, an action can display “Set priority:high” but carry args.value: "low", so checking the advertised proposal performs a different mutation. Construct summaries from the validated payload, or reject summaries that do not match it, so the confirmation surface cannot misrepresent the accepted action.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
skills/backlog-triage/SKILL.md (1)

110-144: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

모델 액션의 공통 필수 필드를 문서화하세요.

제공된 triage-report.js, Lines 645-704는 모든 섹션에 비어 있지 않은 summary를 요구하고, milestone 액션에는 최상위 sprintName을 요구합니다. 현재 Line 110은 relationship 액션의 summary와 milestone 액션의 최상위 sprintName을 명시하지 않습니다. 모델이 이 설명만 따르면 보고서 검증에서 거부될 수 있습니다. 모든 액션의 공통 필드와 milestone 전용 필드를 문장으로 명시하세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/backlog-triage/SKILL.md` around lines 110 - 144, Update the
model-judged action documentation to state that every action requires a
non-empty top-level summary, and that milestone actions additionally require the
top-level sprintName field. Ensure the relationship example and the milestone
field requirements match the validation enforced by triage-report.js.
🧹 Nitpick comments (1)
skills/backlog-triage/scripts/triage-report.js (1)

697-699: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

조건식을 괄호로 명확하게 표현하세요.

Line 697은 &&||를 괄호 없이 혼합합니다. 연산자 우선순위 때문에 동작은 의도와 같지만, 의도를 읽기 어렵습니다. 두 번째 검사는 첫 번째 검사를 포함하므로 하나의 조건으로 줄일 수 있습니다.

♻️ 제안 리팩터
-  if (section === "milestone" && typeof action.sprintName !== "string" || section === "milestone" && !String(action.sprintName || "").trim()) {
+  if (section === "milestone" && (typeof action.sprintName !== "string" || action.sprintName.trim() === "")) {
     throw new Error(`${label} (assign-milestone) requires a non-empty top-level sprintName for grouping.`);
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/backlog-triage/scripts/triage-report.js` around lines 697 - 699,
Update the validation condition in the milestone handling branch to group the
section check once and combine it with the sprintName validity checks using
explicit parentheses. Preserve the requirement that section is "milestone" and
action.sprintName must be a non-empty string, while removing the redundant
repeated section comparison.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@skills/backlog-triage/scripts/triage-report.js`:
- Around line 580-582: Apply dedupeActions to the priorityActions and
milestoneActions results in the report generation flow, alongside the existing
obsoleteActions handling, before rendering section-specific lists. Preserve the
existing allActions aggregation while ensuring each section removes duplicate
actions using the same (verb, issueNumber, args) criteria.
- Around line 611-624: Update mergeModelRelationships so that after appending
model relationships to base.edges, it sorts the merged edges using the same
ordering as renderRelationships and removes duplicate edges according to
triage-relate.js’s edgeIdentity(...) criteria. Preserve the existing edge
normalization and relate object handling.

In `@skills/backlog-triage/scripts/triage-report.test.js`:
- Around line 822-849: Update the test using buildReportModel so both duplicate
set-priority actions use a section permitted for that verb by
MODEL_SECTION_VERBS, while retaining the same (verb, issueNumber, args) values
and dedupe assertion. Ensure the fixture represents valid loadModelActions input
and still verifies only one apply entry.

In `@skills/backlog-triage/SKILL.md`:
- Around line 28-29: Update the model-judgment descriptions in the
Report/Analyze, model-action guidance, and Report/Render sections to explicitly
include obsolete actions. State that model-generated obsolete actions are
combined with deterministic stale signals by triage-report.js, while preserving
the existing descriptions of relationship, priority, and milestone decisions.

---

Outside diff comments:
In `@skills/backlog-triage/SKILL.md`:
- Around line 110-144: Update the model-judged action documentation to state
that every action requires a non-empty top-level summary, and that milestone
actions additionally require the top-level sprintName field. Ensure the
relationship example and the milestone field requirements match the validation
enforced by triage-report.js.

---

Nitpick comments:
In `@skills/backlog-triage/scripts/triage-report.js`:
- Around line 697-699: Update the validation condition in the milestone handling
branch to group the section check once and combine it with the sprintName
validity checks using explicit parentheses. Preserve the requirement that
section is "milestone" and action.sprintName must be a non-empty string, while
removing the redundant repeated section comparison.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ef19f264-8a12-46f1-b415-e5c88f7c7531

📥 Commits

Reviewing files that changed from the base of the PR and between d0b12f8 and 4ff2f45.

📒 Files selected for processing (14)
  • CHANGELOG.md
  • README.md
  • backlog/triage-config.yml
  • skills/backlog-triage/SKILL.md
  • skills/backlog-triage/references/relationships.md
  • skills/backlog-triage/references/stale.md
  • skills/backlog-triage/scripts/triage-collect.test.js
  • skills/backlog-triage/scripts/triage-relate.js
  • skills/backlog-triage/scripts/triage-relate.test.js
  • skills/backlog-triage/scripts/triage-report.js
  • skills/backlog-triage/scripts/triage-report.test.js
  • skills/dev-backlog/references/workflow-patterns.md
  • skills/dev-backlog/scripts/lib.js
  • skills/dev-backlog/scripts/lib.test.js
💤 Files with no reviewable changes (4)
  • backlog/triage-config.yml
  • skills/backlog-triage/scripts/triage-collect.test.js
  • skills/dev-backlog/scripts/lib.js
  • skills/backlog-triage/scripts/triage-relate.js
🚧 Files skipped from review as they are similar to previous changes (3)
  • CHANGELOG.md
  • skills/backlog-triage/references/stale.md
  • skills/backlog-triage/references/relationships.md

Comment thread skills/backlog-triage/scripts/triage-report.js Outdated
Comment on lines +611 to +624
const base = relate && Array.isArray(relate.edges)
? { ...relate, edges: [...relate.edges] }
: { edges: [] };

for (const action of modelRelationships) {
const edge = action.args;
base.edges.push({
from: edge.from,
to: edge.to,
kind: edge.kind,
confidence: Number.isFinite(edge.confidence) ? edge.confidence : 1,
evidence: typeof edge.evidence === "object" && edge.evidence !== null ? edge.evidence : {},
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# renderRelationships와 sortEdges의 구현 및 relate 객체 사용 필드를 확인
fd -t f 'triage-relate.js|triage-report.js' skills/backlog-triage/scripts --exec ast-grep outline {} --items all

rg -nP -C 8 'function renderRelationships|function sortEdges' skills/backlog-triage/scripts
rg -nP -C 3 '\brelate\.(?!edges)\w+' skills/backlog-triage/scripts

Repository: sungjunlee/dev-backlog

Length of output: 9866


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== triage-report.js relevant sections =="
sed -n '1,220p' skills/backlog-triage/scripts/triage-report.js
sed -n '220,380p' skills/backlog-triage/scripts/triage-report.js
sed -n '600,665p' skills/backlog-triage/scripts/triage-report.js

echo
echo "== triage-relate.js relevant sections =="
sed -n '1,190p' skills/backlog-triage/scripts/triage-relate.js
sed -n '270,340p' skills/backlog-triage/scripts/triage-relate.js

echo
echo "== all relate field references =="
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' '\brelate\b|\.\b(relate|generated|kindEtc|extra|model)\b' skills/backlog-triage 2>/dev/null | head -200

Repository: sungjunlee/dev-backlog

Length of output: 30243


병합 후 엣지를 정렬하고 같은 엣지를 제거하세요.

renderRelationshipsrelate.edges를 정렬하고 렌더링하지만, mergeModelRelationships는 모델 엣지를 끝에만 추가합니다. 결정론적이게 만들려면 병합한 배열 다시 정렬하고, triage-relate.jsedgeIdentity(...) 기준으로 동등한 엣지를 제거해야 합니다.

현재 relate 객체에는 edges만 참조되므로 generated 같은 필드가 깨질 우려는 없습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/backlog-triage/scripts/triage-report.js` around lines 611 - 624,
Update mergeModelRelationships so that after appending model relationships to
base.edges, it sorts the merged edges using the same ordering as
renderRelationships and removes duplicate edges according to triage-relate.js’s
edgeIdentity(...) criteria. Preserve the existing edge normalization and relate
object handling.

Comment thread skills/backlog-triage/scripts/triage-report.test.js
Comment on lines +28 to +29
| Report | Analyze | Classification and deterministic signals come from scripts; the model judges blocks/depends-on/duplicates, priority, and milestone actions from the snapshot and writes them to a `--model-actions` JSON file. Alignment and Decision Review are prompt-driven from the same evidence. |
| Report | Render | `triage-report.js` validates the model actions, merges them with deterministic signals, and writes one markdown report with anchored proposals and a consolidated Apply Checklist. |

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

obsolete 모델 액션을 분석 및 판단 분포에 명시하세요.

제공된 triage-report.js, Lines 567-606은 section === "obsolete" 모델 액션을 결정론적 stale 액션과 병합합니다. 그러나 Lines 28-29, 71-80, 148-152는 모델 판단을 관계, 우선순위, 마일스톤으로만 설명합니다. 이 불일치는 모델이 유효한 obsolete 액션을 생성하지 않게 할 수 있습니다. 세 설명에 obsolete 모델 액션과 결정론적 stale 신호의 관계를 명시하세요.

Also applies to: 71-80, 148-152

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/backlog-triage/SKILL.md` around lines 28 - 29, Update the
model-judgment descriptions in the Report/Analyze, model-action guidance, and
Report/Render sections to explicitly include obsolete actions. State that
model-generated obsolete actions are combined with deterministic stale signals
by triage-report.js, while preserving the existing descriptions of relationship,
priority, and milestone decisions.

Round-3 review (both reviewers NOT-LGTM on a single P1):

- move module.exports above the main() invocation so the CLI path
  populates exports before dedupeActions' deferred require of
  triage-apply.js; verified no warnings on a real CLI run
- dedupe priority/milestone section arrays too so the report stays
  internally consistent with the Apply Checklist
- simplify the milestone sprintName check
- test: add a dedupe case for args differing only by key order and
  whitespace (locks in normalizeArgs/stableSerialize parity with apply)

468 tests pass / 0 fail; smoke-test 190 pass.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3c6cea41ed

ℹ️ 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".

Comment on lines +690 to +694
for (const key of requiredArgs) {
const value = args[key];
if (typeof value !== "string" || value.trim() === "") {
throw new Error(`${label} (${verb}) requires a non-empty string arg "${key}".`);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep model action arguments on one line

When a model emits a multiline mutation argument—for example, a reason containing a newline—this validation accepts it, but formatAnchor() inserts the newline literally into the HTML anchor. Since parseReport() passes one line at a time to parseAnchor(), neither copy of that anchor is recognized, so checking the visibly rendered proposal never applies the action. Reject or normalize line breaks in anchor-bound model arguments before rendering.

Useful? React with 👍 / 👎.

Round-4 review: codex flagged P1 (numeric sprintName passed validation then
coerced to undefined, rendering a literal '### undefined' milestone group);
claude agreed as a suggestion. Restore the typeof check in
validateModelAction and add regression tests:

- reject numeric sprintName for milestone actions
- dedupe two same-key actions within the priority section itself
  (locks section-level dedupe, not just the Apply Checklist)

470 tests pass / 0 fail; smoke-test 190 pass.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3d16ca94a4

ℹ️ 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".

Comment thread README.md
Comment on lines +313 to +314
# model-authored: blocks/depends-on/duplicate edges + priority/milestone actions
node $SKILL/triage-report.js --snapshot $SNAP --relate /tmp/relate.json --stale /tmp/stale.json --model-actions /tmp/model-actions.json

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Collect closed issues before judging duplicates

In the documented workflow, triage-collect.js is still invoked without --with-closed-issues, so the snapshot contains no closed_issues evidence even though this new model step advertises duplicate edges and replaces duplicate-of-closed detection. Because the skill defines the snapshot as the canonical input and forbids downstream re-fetches, following this command sequence cannot produce duplicate-of-closed proposals; add the collection flag to this workflow (and the matching workflow-patterns example) or narrow the advertised scope to open/open duplicates.

Useful? React with 👍 / 👎.

Comment on lines +621 to +622
confidence: Number.isFinite(edge.confidence) ? edge.confidence : 1,
evidence: typeof edge.evidence === "object" && edge.evidence !== null ? edge.evidence : {},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Render the semantic duplicate rationale

When a documented model duplicate-candidate supplies evidence.reason but omits the optional confidence, this conversion invents confidence 1; the existing duplicate renderer then ignores reason and prints only score 1.00 because it still expects the removed Jaccard overlap evidence. The resulting report hides the model's actual rationale and presents an unsupported maximum score, so translate the new semantic evidence into the rendered line rather than defaulting it to the old heuristic shape.

Useful? React with 👍 / 👎.

Comment on lines +680 to +682
const requiredArgs = section === "priority"
? ["value"]
: section === "milestone"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restrict priority proposals to the supported value

When the model emits a typo or any value other than the rubric's required high, this validation accepts it as long as it is non-empty. If the proposal is checked, triage-apply.js constructs and adds priority:<value> while removing the existing priority label; this either fails on a repository without that label and stops later accepted actions, or applies a priority outside the advertised proposal semantics. Validate args.value === "high" before rendering the anchor.

Useful? React with 👍 / 👎.

Claude round-5 suggestion: cluster had the same validate/load asymmetry
as sprintName — a non-string cluster passed validation, was coerced to
undefined, and silently collapsed into the uncategorized group. Reject a
non-string cluster when present so the optional-field surface is uniform.

470 tests pass / 0 fail; smoke-test 190 pass.
@sungjunlee
sungjunlee merged commit 9d61400 into main Aug 8, 2026
3 checks passed
@sungjunlee
sungjunlee deleted the refactor/backlog-triage-judgment-to-prompt branch August 8, 2026 11:58

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 57d2d382a3

ℹ️ 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".

const modelRelationships = modelActions.filter((action) => action.section === "relationship");
const mergedRelate = mergeModelRelationships(relate, modelRelationships);

const priorityActions = dedupeActions(modelActions.filter((action) => action.section === "priority"));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve critical priority labels

When a snapshot issue already has priority:critical, a model-generated set-priority action with the documented value high is retained here even though the proposal rubric excludes critical issues. If checked, triage-apply.js removes the existing priority label and adds priority:high, silently downgrading the issue; the removed buildPriorityActions explicitly skipped both high and critical issues, so retain that snapshot-based eligibility check when accepting model actions.

Useful? React with 👍 / 👎.

const mergedRelate = mergeModelRelationships(relate, modelRelationships);

const priorityActions = dedupeActions(modelActions.filter((action) => action.section === "priority"));
const milestoneActions = dedupeActions(modelActions.filter((action) => action.section === "milestone"));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Do not overwrite an existing milestone

When the model proposes assign-milestone for an issue that already has a milestone in the snapshot, this accepts and renders the action without checking current state. Checking it causes triage-apply.js to replace the existing milestone, despite the documented model rubric requiring an unassigned issue; the removed milestone builder skipped every issue whose milestone was non-null, so preserve that guard for model actions.

Useful? React with 👍 / 👎.

Comment on lines +580 to +581
const priorityActions = dedupeActions(modelActions.filter((action) => action.section === "priority"));
const milestoneActions = dedupeActions(modelActions.filter((action) => action.section === "milestone"));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Exclude actions for issues already proposed for closure

When deterministic stale analysis proposes closing an issue but the model also emits a priority or milestone action for it, these unfiltered lists retain all proposals. If the reviewer checks them, apply can close the issue and then relabel or milestone that closed issue; the removed builders used the obsolete-action set to exclude such issues, matching the new rubric's “not already proposed for close” requirement, so filter both lists against closing obsolete actions before rendering.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

backlog-triage: replace judgment heuristics with prompt guidance (Option B)

1 participant