Conversation
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configuration
Comment |
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: copejon The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
9ad25e7 to
799f404
Compare
brandisher
left a comment
There was a problem hiding this comment.
Strong test coverage and the collectors are appropriately narrow in what data they pull. But stepping back, this plugin has ~2,700 lines of pipeline code (collectors, metrics, rendering, CSV export) trying to deterministically pre-solve a lot of problems that the model is already good at solving live: calling gh/Jira APIs in a loop per team member, aggregating results, formatting a table or summary. A skill doesn't need a hand-built GraphQL batching engine, a custom Jira REST client, or a 1,100-line renderer with duplicated text/markdown code paths — it can just tell the model what data to gather and how to present it, and let it write and run that logic on the fly each time.
Suggest cutting this down significantly: lean on gh and the mcp-atlassian MCP tools directly instead of reimplementing their transports, and trust the model to loop over the roster, compute the aggregates, and render the output per-invocation rather than shipping a large deterministic pipeline to maintain. Specific spots called out inline, but the overall ask is to simplify broadly, not just at these points.
| name: str | ||
| github: str | ||
| role: str | ||
| location: str |
There was a problem hiding this comment.
location is parsed from the roster and serialized into roster.json (via asdict(member) at line 224), but nothing downstream (metrics.py, render.py, report.py, export_csv.py) ever reads it. Same for kerberos as a standalone field — it's only needed transiently to build jira_username (line 105), so persisting it separately is redundant. Suggest dropping both from the Member dataclass, or at least excluding them from the serialized output, so we're not carrying extra personal fields into roster.json for no reason.
| # Managers on the Eng/QE roster by role title who are not IC contributors. | ||
| # Filtering them from the team median prevents distortion of allocation signals. | ||
| # Stored as the hash256 of the member's jira username | ||
| EXCLUDED_MEMBERS = ("82e5282cc07f498ff0daf8352e7403e2f16b017dd17c3e589276e406480bb5be" |
There was a problem hiding this comment.
These are SHA-256 hashes of manager jira_usernames, presumably so the names aren't readable directly in source. But the roster itself (roster.json) has these same people in plaintext, and the comment above already explains why they're excluded (managers skew the team median). Hashing here doesn't add real privacy — it just makes the exclusion list unreviewable at a glance in code review, and brittle if anyone's jira username changes. Would a plaintext list (or pulling the exclusion from the role field already in the roster, e.g. anyone not is_engineering_or_qe) be simpler and equally safe here?
| return lines | ||
|
|
||
|
|
||
| def _summary_team_text(summary: ExecutiveSummary) -> List[str]: |
There was a problem hiding this comment.
Starting here through ~line 1125, every summary section has a paired _..._text / _..._markdown function (7 pairs: header, team, workstreams, people, data_health, what_counted, how_attributed, what_excluded). They're near-identical aside from formatting tokens (## headers, | tables vs plain lines). That's roughly 400 lines of duplicated logic. Worth collapsing into one function per section that takes a markdown: bool (or format enum) and branches only on the line-formatting, rather than two full copies of the content-building logic.
| @@ -0,0 +1,153 @@ | |||
| # edge-contribution — known gaps | |||
There was a problem hiding this comment.
Suggest dropping this file entirely rather than shipping a roadmap with the plugin. It's a lot of speculative future work (P1/P2/P3 items, live-data validation targets) baked into the repo, and it'll drift out of sync fast. Let's land this plugin as scoped, then plan enhancements based on actual user feedback rather than a pre-written backlog.
There was a problem hiding this comment.
Agreed - this one slipped in but it's only meant for local dev work.
| return JiraConfig(base_url=base_url, username=username, api_token=api_token) | ||
|
|
||
|
|
||
| class JiraClient: |
There was a problem hiding this comment.
This implements a full custom Jira REST client (auth, HTTP, pagination) via requests. This workspace already has mcp-atlassian configured with Jira search tools — could this plugin depend on that instead of maintaining its own HTTP/auth layer? Would cut a meaningful chunk of _common.py plus the test_common.py coverage for it.
There was a problem hiding this comment.
That's a good point. I don't see that wouldn't work. Will try and see
There was a problem hiding this comment.
The short answer is "kind of." Models communicate w/ mcps over jsonrpc 2.0. So instead of implementing a REST client here, it would be a jsonrpc2.0 client, add dependency on another plugin, but gain nothing (AFAICT).
| return f"{window.start.isoformat()}..{window.end.isoformat()}" | ||
|
|
||
|
|
||
| def build_batch_query(slots: List[SearchSlot], window: Window) -> str: |
There was a problem hiding this comment.
Suggest dropping the custom batching layer here in favor of plain gh calls — SearchSlot, build_batch_query, parse_batch_response, and graphql_rate_limit_adapter (~350 lines across lines 83–430) amount to a bespoke GraphQL query engine built on top of gh api graphql. gh search prs or per-member gh api calls would get the same data with a fraction of the code and none of the custom rate-limit/parsing logic to maintain. Team-roster-sized workloads shouldn't need this level of batching — let's lean on gh directly and cut this.
There was a problem hiding this comment.
I started out with using gh directly, but ran into some limitations that forced me onto graphql. Namely because running 1 gh search per user was consistently triggering API rate limiting. Batching via graphql has eliminated that problem.
…map) Introduce the edge-contribution Claude Code plugin: two skills over a shared, standalone, testable data-collection layer that report cross-workstream contribution for a quarter or date range. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
799f404 to
5604d33
Compare
The location and kerberos fields were parsed from the roster and serialized to roster.json, but nothing downstream (metrics.py, render.py, report.py, export_csv.py) ever read them. kerberos is still derived from the Rover URL and used transiently to build jira_username, but it's no longer persisted as a separate field. location is dropped entirely. Addresses code review feedback on PR openshift-eng#300. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Previously, each summary section had two functions: _foo_text and _foo_markdown, duplicating logic with only formatting differences (markdown headers, pipe tables vs plain text). This resulted in 16 functions (8 pairs) with duplicated code. Now each section has one function with a format parameter that branches only on the line-formatting tokens. Output is byte-for-byte identical to the previous implementation. Functions refactored: - _summary_header (was _summary_header_text + _summary_header_markdown) - _summary_team - _summary_workstreams - _summary_people - _summary_data_health - _summary_what_counted - _summary_how_attributed - _summary_what_excluded Reduces render.py from 1143 to 1112 lines. All 408 tests pass. Addresses code review feedback on PR openshift-eng#300. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This was a local development planning file that accidentally slipped into the PR. Removing per code review feedback. Addresses code review feedback on PR openshift-eng#300. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add the edge-contribution Claude Code plugin: 3 skills providing a manager-user 2 different views of edge-team member work distribution across work streams. Users are provided a data-driven analysis of contribution diversity (how many workstreams a member contributed to) and workstream silo-ing (how many contributions each workstream received). Accepts times spans in FYQ (e.g.
2026Q1), quarter-to-date, and arbitrary time spans.Skills

/edge-contribution:heatmap/edge-contribution:summary/edge-contribution:exportExports the raw data in csv format