A story is meant to be the whole life of a unit of work — issue, agent runs, human gates, PRs, the GitHub conversation — folded into one chronological timeline (apps/web/lib/story.ts:13-17). Today it cannot be, because Facility never mirrors pull requests.
services/api/src/github/issues-sync.ts drops them at all three entry points — GitHub's REST issues endpoints return PRs alongside issues, and we filter them out:
if (!owner || !name || !issue?.number || issue.pull_request) return null; // :55 webhook
if (!owner || !name || !issueNumber || payload.issue?.pull_request) return null; // :69 comments
if (issue.pull_request) continue; // :133 backfill
There is no PR table. A PR is known only derivatively: in runs.gh.pr when a Facility builder opened it, and in outcomes once it has closed. A pull request opened directly on GitHub is invisible to Facility — it is not on the board, not in a story, and not in any count.
Three consequences follow from that one gap:
- CI state never reaches a story. The App already holds
Actions: read and Checks: read and subscribes to check_run and workflow_run, but those events go to processOperationalSignal (services/api/src/github/processor.ts:752), which raises repo-level watchtower issues. processWorkflowRun (:450) acts only when the workflow is named facility-* and the conclusion is failure. The repository's own CI never touches pipeline state, and the Checks and Commit Statuses APIs are never called.
- PR↔issue relationships are not read. GitHub resolves
Closes #123 itself and exposes the result as closingIssuesReferences. We already run that query — in services/api/src/watchtower/github.ts:111-161, but on a bare GITHUB_TOKEN client (:52-54) and only during the nightly outcomes reconciliation, far too late for a live board.
- The board cannot distinguish "a machine is still checking this" from "a human should review this". Everything with a PR sits in
In review.
What this epic proposes
Make pull requests first-class in the mirror, and let what happens to them move a story.
The product rule these are designed against: an issue is a story, and every PR associated with that issue is part of the same story. A pull request with no associated issue is its own story. A story is the complete implementation history from the issue to the close, and everything in between.
Sub-issues
The foundation
What that makes possible
Prerequisites and cleanup surfaced along the way
Each sub-issue stands on its own and states the alternatives considered, so they can be argued with — or dropped — individually. #70 and #72 are independent of the rest and could land first.
Not proposed here
- Publishing Facility's own checks back to GitHub via the Commit Statuses API, so branch protection could gate on them. It is the natural counterpart to reading CI, but it is a separate piece of work.
- Cross-repository closing references (
Closes owner/other#5).
Findings are against 0f646ac. Line references are to that commit.
A story is meant to be the whole life of a unit of work — issue, agent runs, human gates, PRs, the GitHub conversation — folded into one chronological timeline (
apps/web/lib/story.ts:13-17). Today it cannot be, because Facility never mirrors pull requests.services/api/src/github/issues-sync.tsdrops them at all three entry points — GitHub's REST issues endpoints return PRs alongside issues, and we filter them out:There is no PR table. A PR is known only derivatively: in
runs.gh.prwhen a Facility builder opened it, and inoutcomesonce it has closed. A pull request opened directly on GitHub is invisible to Facility — it is not on the board, not in a story, and not in any count.Three consequences follow from that one gap:
Actions: readandChecks: readand subscribes tocheck_runandworkflow_run, but those events go toprocessOperationalSignal(services/api/src/github/processor.ts:752), which raises repo-level watchtower issues.processWorkflowRun(:450) acts only when the workflow is namedfacility-*and the conclusion isfailure. The repository's own CI never touches pipeline state, and the Checks and Commit Statuses APIs are never called.Closes #123itself and exposes the result asclosingIssuesReferences. We already run that query — inservices/api/src/watchtower/github.ts:111-161, but on a bareGITHUB_TOKENclient (:52-54) and only during the nightly outcomes reconciliation, far too late for a live board.In review.What this epic proposes
Make pull requests first-class in the mirror, and let what happens to them move a story.
The product rule these are designed against: an issue is a story, and every PR associated with that issue is part of the same story. A pull request with no associated issue is its own story. A story is the complete implementation history from the issue to the close, and everything in between.
Sub-issues
The foundation
What that makes possible
Validatingstage, and flag stories whose CI is redPrerequisites and cleanup surfaced along the way
check_runsignals are fingerprinted per repo, so a green PR resolves a red default branch (blocks Add a Validating stage, and flag stories whose CI is red #68 — CI would otherwise be reported in two contradictory places)Each sub-issue stands on its own and states the alternatives considered, so they can be argued with — or dropped — individually. #70 and #72 are independent of the rest and could land first.
Not proposed here
Closes owner/other#5).Findings are against
0f646ac. Line references are to that commit.