← All entries
Web app

PR Finder Dashboard

Browse accumulated PR-per-Objective reports with a PI/sprint report picker — which pull requests actually closed the work under each Objective.

Web app pull-requestsobjectivessub-issuescross-repouswdsstaticprojects-v2

◎ What it solves

Follows an Objective's sub-issue tree across repos, orgs and projects — the one thing GitHub's own UI won't do — and reports the PRs that actually closed the work.

⚠ Limitations & Risks

Every catalog entry must acknowledge at least one limitation or risk.

What it solves

Answering “what actually shipped under this Objective?” used to mean walking the tree by hand: open the Objective, expand its sub-issues, click into each child, check whether a PR closed it, note it down, go back, do the next one. Five levels deep, that is dozens of clicks to answer one question — and it has to be redone every sprint.

But the clicking is the smaller problem. The tree does not stay in one repo. An Objective’s work spills into other repositories, other orgs, and other projects, and GitHub’s own UI will not show you a sub-issue tree that crosses those boundaries. So the manual walk doesn’t just take an afternoon — it goes cold at the first boundary. You end up with a partial answer and no way to know what you missed.

The crawler doesn’t care where a child lives. Every node it fetches carries its own repository.nameWithOwner, so traversal simply continues across repos and orgs; a repo you can’t read degrades to empty children instead of aborting the whole crawl. One pass, one report, whatever boundaries the work happens to straddle.

A sub-issue tree that crosses repositories, walked by hand versus crawled

Before: the tree spans three repositories in different orgs; a person must switch context at each boundary and the trail goes cold. After: one crawl threads straight through every boundary and emits a single report of the closing pull requests.

BEFORE — by handrepo-arepo-bother-org / repo-c🎯 Objective #101↳ #118 sub-issue↳ #121 sub-issue✋ different repo✋ different org⚠ the trail goes cold at each boundaryclick · expand · check · go back · repeatAFTER — one crawlrepo-arepo-bother-org / repo-c🎯 Objective #101↳ #118 · closed by #204↳ #121 · closed by #211boundaries crossed, not stopped atObjective #101 — closing PRs

The tree is the same on both sides — what changes is whether a repository boundary is a wall or just another edge to follow. Each node carries its own repository.nameWithOwner, so one crawl spans repos, orgs and projects and emits a single report.

What it does

Answers “what actually shipped under this Objective?”. For every Objective on a board, the PR Finder action walks the sub-issue tree — up to 5 levels, across repos and orgs — and lists the pull requests that close those issues. This dashboard is the reading surface: a report picker across accumulated PI/sprint-named reports, each rendered as USWDS-styled HTML.

The relationship it follows is closedByPullRequestsReferences(includeClosedPrs: true) — the closes #N / linked-branch link, not loose cross-references. includeClosedPrs also surfaces PRs that were closed without merging.

Where the data comes from

.github/workflows/pr-finder.yml runs the action weekly and publishes CSV + Markdown + HTML to the pr-finder/report branch, accumulating one report per PI/sprint plus a reports/index.json. The shell fetches that index at runtime into the picker, then loads the chosen report HTML into an iframe via srcdoc. It falls back to the bundled index.snapshot.json + pr_finder_all.html.

Run it locally

# The dashboard is static — just open it.
open pr-dashboard/index.html

# Regenerate a report offline against a canned tree (no network):
python pr-finder/generate_pr_finder.py \
  --tree-json seed/sample_tree.json --out-dir reports