fix: Detect active epics via sub-issue activity - #21
Conversation
clawgenti
left a comment
There was a problem hiding this comment.
Sub-issue-based detection is a clear improvement over the board-Status gate — epics parked in custom columns will no longer be silently dropped, and the recency ranking prevents a single bulk-close epic from crowding the list.
One suggestion on API efficiency, and a minor guard nit.
Reviewed by clawgenti using github:pr-review
| 'latest_closed_at': '', 'total': 0} | ||
| try: | ||
| base = ['gh', 'api', f'repos/{org}/{repo}/issues/{epic_number}/sub_issues', '--paginate'] | ||
| open_res = subprocess.run(base + ['--jq', open_filter], capture_output=True, text=True, timeout=20) |
There was a problem hiding this comment.
suggestion: get_sub_issue_activity makes 4 separate paginated API calls to the same sub_issues endpoint (open count, closed numbers, latest closed_at, total). Each call fetches all pages independently, multiplying API usage by 4x per epic. Consider fetching once into a list and filtering locally — this also eliminates the edge where the 4 calls see different snapshots if sub-issues change mid-run.
|
|
||
| # --paginate concatenates one result per page; sum the per-page ints. | ||
| def _sum_ints(text): | ||
| return sum(int(x) for x in text.split() if x.strip().lstrip('-').isdigit()) |
There was a problem hiding this comment.
nit: x.strip().lstrip('-').isdigit() admits negative integers (e.g. -5 would pass and be summed). Since length always returns >= 0 this is harmless in practice, but the intent is to sum non-negative page counts. x.strip().isdigit() is cleaner and more accurately reflects the invariant.
The weekly report gated epic inclusion on the project board Status column, so epics parked in an "Epics" column (not "In Progress") were dropped or shown as "no activity". Detection is now sub-issue-based: an epic is active if it has open sub-issues or a sub-issue closed within the reporting window. This uses the plain REST sub_issues endpoint and needs no read:project scope; board Status becomes display-only. Epics are ranked by recency of sub-issue closure so a single bulk-close epic does not crowd out low-volume but freshly-active epics. Also registers the github-weekly-report skill in marketplace.json and the README, which were missing it. Assisted-By: Claude Code (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Gloire Rubambiza <gloire@ibm.com>
Address PR review: get_sub_issue_activity made 4 separate paginated
calls to the same sub_issues endpoint (open, closed numbers, latest,
total), multiplying API usage 4x per epic and risking mid-run snapshot
skew. Fetch once, projecting only {number, state, closed_at} server-side
(keeps control-char safety for raw bodies), then filter locally via a
closed_in_window helper. Also drops the negative-admitting isdigit guard
the per-page summing needed.
Assisted-By: Claude Code (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Gloire Rubambiza <gloire@ibm.com>
8d48c03 to
5b47246
Compare
|
Thanks for the review. Addressed both in 5b47246:
Re-verified against the same window ( Assisted-By: Claude Code |
cwiklik
left a comment
There was a problem hiding this comment.
Clean, well-documented refactor. Detection moves from board-status-gated to sub-issue-based (open sub-issues, in-window closures, or merged-PR cross-refs), so it no longer needs read:project. get_sub_issue_activity() uses a single paginated gh api call with server-side jq projection and filters locally — nice touch on avoiding both control-char parse breakage and the 4x API cost.
Correctness spot-checks all pass:
- The mixed int/string sort tuple is type-consistent per position (no TypeError), and
reverse=Trueranking bylatest_closed_atrecency behaves as documented. - The ISO-8601
Z-suffixed lexical range check inclosed_in_windowis valid; nullclosed_atis correctly excluded. - Graceful degradation on non-zero exit / timeout / missing sub_issues endpoint (returns empty → epic simply uncounted).
- Docs match code:
--max-epics(SKILL.md) is implemented with default 10 and the cap is applied after the sort.
CI green (DCO + title check), both commits signed off. One non-blocking suggestion inline. LGTM.
Assisted-By: Claude Code
| # An epic is active if it has open sub-issues (backlog / in-progress), | ||
| # a sub-issue closed this window, or a merged-PR cross-reference this | ||
| # window. Board Status is display-only enrichment — never a gate. | ||
| is_active = sub['open'] > 0 or sub['closed_recent'] > 0 or activity['prs_merged'] > 0 |
There was a problem hiding this comment.
suggestion (non-blocking): intentional behavior change worth a conscious confirm. The old fallback path included any epic updated_in_period, but is_active now requires open sub-issues, an in-window sub-issue closure, or a merged PR. An older-style epic tracked as a plain issue (no sub-issues feature) that only saw comment/label activity this week — with no merged PR — will now drop off the report. That matches the PR's stated intent of making sub-issues the primary signal, so no change needed if all active epics use sub-issues; just flagging it since the updated_in_period inclusion path is gone.
There was a problem hiding this comment.
Yes ,that is the intended new behavior.
Summary
Fixes the weekly report missing active epics. The team now parks active epics in an "Epics" board column rather than "In Progress", so the previous board-Status gate silently dropped them (or showed them as "no activity").
Detection is now sub-issue-based: an epic is active if it has open sub-issues (backlog / in-progress) or a sub-issue closed within the reporting window. This uses the plain REST
sub_issuesendpoint and needs noread:projectscope; board Status becomes display-only enrichment when available.Epics are ranked by recency of sub-issue closure, so a single bulk-close epic does not crowd out low-volume but freshly-active epics. The list is capped (configurable via
--max-epics, default 10) to keep the section a focused planning input.Verification
Against the last report window (
--since 2026-06-22 --until 2026-06-29), all three epics cited in the issue now surface in the default top-10, ranked first by recency:#1789previously showed "no activity". Verified in fallback mode (noread:projecton the local token), which is exactly the path that must work.Scope
Also registers the
github-weekly-reportskill in.claude-plugin/marketplace.jsonand the README Skills table — both were missing it.Fixes #18
Assisted-By: Claude Code