Affected paths and symbols
src/connectors/github/api.ts, mountGitHubWebhookApi() lines 104–118: githubRevisionResolution() is only called when eventName === "issues".
src/runtime/postgres.ts, PostgresRuntimeStore.admitEvent() lines 562–568: a matching Git binding whose revision selector is /repository/defaultBranchRevision/commit requires a revision-resolution identity.
src/runtime/postgres.ts, recoverGitHubRevisionResolution() lines 840–860: the fallback can only recover an installation ID from an already persisted prior event for the repository.
Failure path
- Configure a
github.app.webhook trigger with a binding for com.github.pull_request.opened (or another supported pull-request event).
- Give the binding a Git workspace using the documented default-branch selector:
{
"type":"git",
"repository":{"url":{"path":"/repository/cloneUrl"}},
"revision":{"commit":{"path":"/repository/defaultBranchRevision/commit"}}
}
- Send the repository's first signed
pull_request delivery. Normalization includes installationId, repository ID, full name, clone URL, and default branch in the event data.
- The webhook route does not pass those values as
revisionResolution, because line 104 limits it to issues.
admitEvent() identifies the matching default-branch binding as resolution-gated. Its fallback query runs before the current event is inserted and finds no prior event, so it returns no installation identity. admitEvent() throws WorkspaceResolutionError("Workspace revision resolution requires a GitHub installation identity").
- The webhook handler maps that error to HTTP 422. The delivery is not persisted, no resolution row is created, and no execution can later be created by retrying that same delivery.
Expected behavior
Any supported GitHub webhook whose normalized payload carries a valid installation and repository identity should provide revision-resolution metadata when a matching default-branch Git workspace needs it. The first pull-request delivery should be accepted, persisted with a pending resolution, and create the execution after the resolver returns the default-branch commit.
Actual behavior
Only issue deliveries supply revisionResolution directly. A first pull-request delivery using a default-branch workspace is rejected with 422 despite containing all required GitHub App identity fields.
Reproduction evidence / precise failing-test design
Extend test/e2e/github-webhook-persistence.test.ts using its existing signed pullRequestPayload() and webhook helper:
- Publish a second binding matching
com.github.pull_request.synchronize, but replace the existing head-SHA workspace with the default-branch workspace shown above.
- Use a fresh webhook trigger/repository database so no prior event exists for repository ID
10.
- Submit the existing valid signed pull-request payload, which includes
installation: { id: 44 }, repository acme/widgets, and default branch main.
Expected assertion:
expect((await webhook(app, "first-default-branch-pr", raw)).status).toBe(202);
expect(await store.claimRevisionResolution({
leaseOwner: "resolver", leaseDurationMs: 60_000,
})).toMatchObject({ installationId: 44, repositoryId: 10, branch: "main" });
Current behavior is deterministic: the response is 422 from WorkspaceResolutionError, dispatch_events has no row for the delivery, and claimRevisionResolution() returns undefined. The issues path already demonstrates the required metadata construction in githubRevisionResolution(); removing the event-name gate (while retaining validation) makes the pending resolution reachable for pull-request deliveries.
Finding fingerprint
dispatch/github-webhook-pull-request-default-branch-revision-missing-resolution-metadata-v1
Affected paths and symbols
src/connectors/github/api.ts,mountGitHubWebhookApi()lines 104–118:githubRevisionResolution()is only called wheneventName === "issues".src/runtime/postgres.ts,PostgresRuntimeStore.admitEvent()lines 562–568: a matching Git binding whose revision selector is/repository/defaultBranchRevision/commitrequires a revision-resolution identity.src/runtime/postgres.ts,recoverGitHubRevisionResolution()lines 840–860: the fallback can only recover an installation ID from an already persisted prior event for the repository.Failure path
github.app.webhooktrigger with a binding forcom.github.pull_request.opened(or another supported pull-request event).{ "type":"git", "repository":{"url":{"path":"/repository/cloneUrl"}}, "revision":{"commit":{"path":"/repository/defaultBranchRevision/commit"}} }pull_requestdelivery. Normalization includesinstallationId, repository ID, full name, clone URL, and default branch in the event data.revisionResolution, because line 104 limits it toissues.admitEvent()identifies the matching default-branch binding as resolution-gated. Its fallback query runs before the current event is inserted and finds no prior event, so it returns no installation identity.admitEvent()throwsWorkspaceResolutionError("Workspace revision resolution requires a GitHub installation identity").Expected behavior
Any supported GitHub webhook whose normalized payload carries a valid installation and repository identity should provide revision-resolution metadata when a matching default-branch Git workspace needs it. The first pull-request delivery should be accepted, persisted with a pending resolution, and create the execution after the resolver returns the default-branch commit.
Actual behavior
Only issue deliveries supply
revisionResolutiondirectly. A first pull-request delivery using a default-branch workspace is rejected with 422 despite containing all required GitHub App identity fields.Reproduction evidence / precise failing-test design
Extend
test/e2e/github-webhook-persistence.test.tsusing its existing signedpullRequestPayload()and webhook helper:com.github.pull_request.synchronize, but replace the existing head-SHA workspace with the default-branch workspace shown above.10.installation: { id: 44 }, repositoryacme/widgets, and default branchmain.Expected assertion:
Current behavior is deterministic: the response is 422 from
WorkspaceResolutionError,dispatch_eventshas no row for the delivery, andclaimRevisionResolution()returnsundefined. Theissuespath already demonstrates the required metadata construction ingithubRevisionResolution(); removing the event-name gate (while retaining validation) makes the pending resolution reachable for pull-request deliveries.Finding fingerprint
dispatch/github-webhook-pull-request-default-branch-revision-missing-resolution-metadata-v1