Skip to content

feat: GitHub issues/PRs/CI dashboard#950

Open
Hweinstock wants to merge 12 commits intoaws:mainfrom
Hweinstock:feat/github-dashboard
Open

feat: GitHub issues/PRs/CI dashboard#950
Hweinstock wants to merge 12 commits intoaws:mainfrom
Hweinstock:feat/github-dashboard

Conversation

@Hweinstock
Copy link
Copy Markdown
Contributor

@Hweinstock Hweinstock commented Apr 24, 2026

Problem

The team has no centralized view of issue health, PR review velocity, or CI reliability for the agentcore-cli repo.

GitHub's built-in Insights are shallow: no time-to-review histograms, no label gap analysis, no CI flaky job detection.

Solution

Adds a dashboard generator under .github/dashboard/ that produces three HTML pages (Issues, PRs, CI/Tests) deployed to GitHub Pages via a new workflow.

Architecture:

  • Declarative config (src/config.ts) — add sections/pages by editing config, no code changes needed
  • TypeScript.
  • GitHub REST + GraphQL APIs via gh CLI
  • Chart.js for client-side rendering
  • Static HTML output — no server needed

The page will automatically update on each issue, PR, and once a day.

Testing

  • Generated all three pages against live repo data (262 issues, 682 PRs, 516 CI runs)
  • TypeScript strict mode + eslint clean
  • Verified Chart.js renders, nav links, time window tabs, copy-to-clipboard
  • README with development workflow docs

Screenshots:
image
image

@github-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Apr 24, 2026
@github-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Apr 24, 2026
@Hweinstock Hweinstock force-pushed the feat/github-dashboard branch from 47b984b to 7b8deb7 Compare April 24, 2026 18:09
@github-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Apr 24, 2026
@github-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Apr 24, 2026
@github-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Apr 24, 2026
@github-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Apr 24, 2026
@github-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Apr 24, 2026
@Hweinstock Hweinstock marked this pull request as ready for review April 24, 2026 20:18
@Hweinstock Hweinstock requested a review from a team April 24, 2026 20:18
@Hweinstock
Copy link
Copy Markdown
Contributor Author

/strands review

@github-actions
Copy link
Copy Markdown
Contributor

Code Review Summary

I've reviewed PR #950 and found several issues that need to be addressed before merge. Overall, the architecture is clean and the config-driven approach is excellent, but there are three blocking issues that must be fixed.


⛔ Blocking Issues

1. Missing @types/node dependency

Category: blocking
Files: .github/dashboard/package.json, .github/dashboard/tsconfig.json

TypeScript compilation fails due to missing Node.js type definitions:

$ npx tsc --noEmit --project .github/dashboard/tsconfig.json
.github/dashboard/src/generate.ts(5,56): error TS2591: Cannot find name 'fs'
.github/dashboard/src/github.ts(2,30): error TS2591: Cannot find name 'node:child_process'
# ... 12+ more errors

Fix: Add @types/node to devDependencies in .github/dashboard/package.json:

"devDependencies": {
  "@types/node": "^20.0.0",
  "tsx": "^4.19.0",
  "typescript": "^5.5.0"
}

Then update tsconfig.json to include node types:

{
  "compilerOptions": {
    "types": ["node"]
  }
}

2. No Tests

Category: blocking
Files: All .github/dashboard/src/*.ts files

Per CONTRIBUTING.md:

  1. Ensure local tests pass.

This PR adds ~1000 lines of TypeScript with zero tests. Critical logic in transform.ts (percentiles, histogram bucketing, TTFR/TTM calculations) is completely untested.

Why this matters:

  • percentiles() calculates P90 metrics displayed to users — wrong calculations would mislead the team
  • bucketLabel() formats time ranges — bugs could make charts unreadable
  • parsePRs() computes PR state buckets — errors could misclassify PRs

Fix: Add unit tests for at least the core transformation logic:

  • src/__tests__/transform.test.ts — test percentiles, bucketLabel, buildHistogram, parsePRs, parseIssues
  • src/__tests__/github.test.ts — mock gh CLI and test error handling

See docs/TESTING.md for the testing guide.


3. Security Risk: pull_request_target Trigger

Category: blocking
File: .github/workflows/dashboard.yml:7

The workflow uses pull_request_target:

on:
  pull_request_target:
    types: [opened, closed]

Why this is dangerous:
pull_request_target runs in the context of the base branch with write permissions to contents, pages, and id-token. A malicious PR could:

  • Modify the workflow to exfiltrate secrets
  • Deploy malicious content to GitHub Pages
  • Access the GITHUB_TOKEN with elevated permissions

See GitHub's security hardening guide.

Fix: Either:

  1. Remove the pull_request_target trigger entirely (dashboard doesn't need to update on every PR)
  2. Use pull_request with permissions: read-all instead
  3. If you must use pull_request_target, add explicit checks:
    - name: Check for approved PR
      run: |
        if [[ "${{ github.event.pull_request.author_association }}" != "MEMBER" ]] && \
           [[ "${{ github.event.pull_request.author_association }}" != "OWNER" ]]; then
          echo "Only members can trigger dashboard updates"
          exit 1
        fi

Recommendation: Remove pull_request_target — the dashboard can update on schedule and manual dispatch without needing PR triggers.


💡 Suggestions (Non-Blocking)

4. Hard-Coded Configuration Values

File: .github/dashboard/src/config.ts:136, .github/dashboard/src/github.ts:4

Several magic numbers lack explanation:

  • maxRuns: 900 — why 900? Does this risk API rate limits?
  • maxBuffer: 50 * 1024 * 1024 — why 50MB?
  • perWf = Math.ceil(maxRuns / matched.length) — could fail if matched.length is 0

Suggestion: Add comments or move to a constants file:

// GitHub API pagination: fetches ~900 CI runs across all workflows
// Stays under rate limit (5000/hour) with safety margin
const MAX_CI_RUNS = 900;

// execFileSync buffer: handles large gh API responses (up to 50MB)
// Typical response ~10MB for 900 runs
const MAX_API_BUFFER = 50 * 1024 * 1024;

5. Error Handling for Missing Workflows

File: .github/dashboard/src/github.ts:102

If a workflow name in config doesn't exist, the code silently fetches 0 runs:

const matched = wfList.filter(w => workflowNames.includes(w.name));
// If matched.length === 0, perWf calculation could divide by zero

Suggestion: Validate workflows exist and provide a helpful error:

const matched = wfList.filter(w => workflowNames.includes(w.name));
if (matched.length === 0) {
  throw new Error(
    `No workflows found matching: ${workflowNames.join(', ')}\n` +
    `Available: ${wfList.map(w => w.name).join(', ')}`
  );
}

6. Workflow Triggers Could Be Expensive

File: .github/workflows/dashboard.yml:5-8

The workflow triggers on every issue/PR event:

issues:
  types: [opened, closed, labeled, unlabeled]
pull_request_target:
  types: [opened, closed]

For a repo with 100+ issues/PRs, this could run the dashboard generator dozens of times per day, fetching the full dataset each time (262 issues, 682 PRs, 900 CI runs).

Suggestion:

  • Remove issue/PR triggers — the daily cron and manual dispatch are sufficient
  • Or add concurrency cancellation (already present, good!) and document the cost

7. Missing Architecture Documentation

File: .github/dashboard/README.md

The README covers local development but not the overall architecture or how to extend the dashboard.

Suggestion: Add a section like:

## Architecture

Data flows through five stages:

1. **Fetch** (`src/github.ts`) — Calls `gh` CLI to fetch issues, PRs, CI runs
2. **Parse** (`src/transform.ts:parseIssues/parsePRs`) — Converts GitHub API types to internal types
3. **Transform** (`src/transform.ts:computePage`) — Aggregates data into metrics, charts, tables
4. **Render** (`src/render.ts`) — Generates static HTML with embedded Chart.js
5. **Deploy** (`dashboard.yml`) — Uploads to GitHub Pages

### Adding a New Metric

1. Add the metric to `MetricKey` in `src/types.ts`
2. Add calculation logic to `computeStats()` in `src/transform.ts`
3. Add the metric to a page's `metrics` array in `src/config.ts`

🔍 Nits (Minor Issues)

8. Code Organization: transform.ts is Large

File: .github/dashboard/src/transform.ts

At 400+ lines with multiple responsibilities (parsing, stats, timeline, histogram, tables, CI), this file could be split:

  • src/parsers.ts — parseIssues, parsePRs
  • src/metrics.ts — computeStats, percentiles
  • src/charts.ts — computeTimeline, computeDistribution, computeHistogram
  • src/transform.ts — orchestration (computePage, computeSection)

Not blocking, but would improve maintainability.


9. Client-Side Code Disables TypeScript

File: .github/dashboard/src/client.ts:1-2

/* eslint-disable */
// @ts-nocheck

This is understandable since it's template code with injected values, but it means zero type safety for 400+ lines of client code.

Alternative: Use a typed template system or generate the client code with proper types retained.


10. Dependency Version Pinning

File: .github/dashboard/package.json

"devDependencies": {
  "tsx": "^4.19.0",         // caret range
  "typescript": "^5.5.0"    // caret range
}

Using ^ ranges means npm ci could install different versions in CI vs local, potentially causing build breakage.

Suggestion: Pin exact versions for CI reproducibility:

"devDependencies": {
  "tsx": "4.19.0",
  "typescript": "5.5.0"
}

✅ What I Liked

  • Clean architecture: Separation of concerns is excellent (fetch/parse/transform/render)
  • Config-driven: Easy to add new pages/sections without code changes
  • Type safety: TypeScript strict mode enabled (once @types/node is added)
  • Self-contained output: Copying chart.js locally is smart
  • Sensible metrics: TTFR, TTM, P90s, flaky job detection are all useful

📋 Action Items

Before merge:

  1. ✅ Add @types/node to devDependencies and verify npx tsc --noEmit passes
  2. ✅ Add unit tests for core transformation logic (at minimum percentiles, bucketLabel, parsePRs)
  3. ✅ Remove pull_request_target trigger or add security guards
  4. 📝 Document why maxRuns: 900 was chosen (in code comment or README)
  5. 📝 Add architecture section to README

Nice-to-haves:

  • Consider splitting transform.ts into smaller modules
  • Add constants file for magic numbers (P90 threshold, time windows, etc.)
  • Pin exact versions in package.json for CI reproducibility

Great work on the dashboard concept! The visualizations look excellent in the screenshots. Once the blocking issues are addressed, this will be a valuable tool for the team.

@github-actions github-actions Bot removed the size/xl PR size: XL label Apr 24, 2026
@github-actions github-actions Bot added the size/xl PR size: XL label Apr 24, 2026
@Hweinstock
Copy link
Copy Markdown
Contributor Author

/strands review

@github-actions
Copy link
Copy Markdown
Contributor

Code Review Summary

I've reviewed PR #950 and I'm pleased to report that the previous blocking issues have been fully addressed. The code is well-structured, follows good engineering practices, and includes comprehensive tests. I have a few suggestions for improvement, but none are blocking.


✅ Previous Issues Resolved

  1. @types/node dependency ✓ Added in package.json
  2. Unit tests ✓ Comprehensive test suite in src/__tests__/transform.test.ts with 344 lines covering all core logic
  3. Security risk (pull_request_target) ✓ Removed entirely — workflow now only triggers on schedule and manual dispatch

💡 Suggestions (Non-Blocking)

1. CI Workflow Optimization

File: .github/workflows/dashboard.yml
Category: suggestion

The workflow fetches ~900 CI runs, which may hit GitHub API rate limits under certain conditions. Consider:

- name: Generate dashboard
  working-directory: .github/dashboard
  env:
    GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  run: |
    # Add rate limit check before generation
    REMAINING=$(gh api rate_limit --jq '.resources.core.remaining')
    if [ "$REMAINING" -lt 100 ]; then
      echo "Warning: Only $REMAINING API calls remaining"
    fi
    npx tsx src/generate.ts

Also, the maxRuns: 900 config comment explains the choice well, but you might want to add a note about monitoring API usage in the README.


2. Error Handling for Missing Workflow Names

File: .github/dashboard/src/github.ts:102
Category: suggestion

Good validation was added for empty workflow matches! One additional edge case to consider: what if the workflows array in config is empty?

export function fetchCIRuns(repo: string, workflowNames: string[], branch: string, maxRuns: number): WorkflowRun[] {
  if (workflowNames.length === 0) {
    throw new Error('No workflow names provided in CI configuration');
  }
  // ... rest of implementation
}

3. Test Coverage for Edge Cases

File: .github/dashboard/src/__tests__/transform.test.ts
Category: suggestion

The tests are excellent! A few additional cases to consider:

  • Histogram buckets: Test with buckets array length of 0 or 1
  • ParsePRs: Test with all reviews in DISMISSED or PENDING state
  • ComputeStats: Test metrics with items array containing mixed Issue/PR types (though type filtering should prevent this)

Example:

it('handles empty buckets array', () => {
  const result = buildHistogram([5, 10], []);
  expect(result).toEqual([]);
});

4. Client-Side TypeScript Disabled

File: .github/dashboard/src/client.ts:1-2
Category: nit

/* eslint-disable */
// @ts-nocheck

This is understandable since placeholders are injected at build time, but it means 466 lines have zero type safety. A safer approach would be to:

  1. Extract injected values to a separate minimal file
  2. Keep the rest of client.ts with full type checking

Not blocking, but worth considering for future maintenance.


5. Dependency Version Pinning

File: .github/dashboard/package.json
Category: nit

The previous review suggested pinning exact versions. You've kept caret ranges:

"devDependencies": {
  "@types/node": "20.17.57",  // No caret - good!
  "tsx": "4.19.4",             // No caret - good!
  "typescript": "5.8.3",       // No caret - good!
  "vitest": "3.2.1"            // No caret - good!
}

Actually, I see you did pin exact versions — well done! The package-lock.json will ensure reproducible builds.


6. Documentation Enhancement

File: .github/dashboard/README.md
Category: suggestion

The README covers local development well. Consider adding:

  • Architecture overview — data flow from GitHub API → TypeScript → HTML/Chart.js
  • Extending the dashboard — step-by-step guide for adding a new metric or chart
  • Troubleshooting — common errors (rate limits, gh CLI auth, missing workflows)

The previous review had a good architecture section example that would fit nicely here.


🎯 What I Liked

  • Comprehensive testing: 344-line test suite with excellent coverage of edge cases
  • Type safety: Full TypeScript strict mode throughout (except templated client code)
  • Clean separation of concerns: github.ts (fetch), transform.ts (compute), render.ts (HTML generation)
  • Self-contained output: Copying Chart.js locally means the dashboard works without external CDN dependencies
  • Config-driven design: Easy to add new pages/sections by editing config.ts without touching rendering logic
  • Security-conscious: Removed pull_request_target trigger entirely
  • Vitest integration: Modern, fast test framework with good DX

📋 Summary

This is ready to merge after addressing the previous review feedback. All three blocking issues are resolved:

  1. ✅ TypeScript compilation now works with @types/node
  2. ✅ Unit tests provide solid coverage of core transformation logic
  3. ✅ Security risk eliminated by removing pull_request_target

The suggestions above are minor improvements for maintainability and robustness, not blockers.

Recommendation:Approve (pending final CI checks)

Great work on the dashboard! The visualizations in the screenshots look polished, and the code quality is high. This will be a valuable tool for tracking repo health.

@github-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Apr 24, 2026
Comment thread .github/dashboard/src/generate.tsx Fixed
…e handlers, use esbuild for TS strip, tighten types
@github-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Apr 24, 2026
@Hweinstock Hweinstock deployed to e2e-testing April 24, 2026 22:58 — with GitHub Actions Active
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/xl PR size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants