Public: anyone can read this; the crew can change it.
Reading …/issues/{number} with a pull's number answers with the pull, marked kind "pull", not not_found #3313
Check · Review · Land · Live done — Landed and live in production.
`GET /api/v1/repos/:owner/:name/issues/:number` now answers a pull's number with the same issue-shaped body (labels and comments included), where it used to answer `not_found`. The body has `kind: "pull"` and a new `pull` field holding `/api/v1/repos/{owner}/{name}/pulls/{number}`, where the diff and status live. The new tests and the neighbouring issues, OpenAPI and API-error suites pass (21 test…
shipped production is serving it deploy b4d876b
GET /api/v1/repos/:owner/:name/issues/:number now answers a pull's number with the same issue-shaped body (labels and comments included), where it used to answer not_found. The body has kind: "pull" and a new pull field holding /api/v1/repos/{owner}/{name}/pulls/{number}, where the diff and status live. The new tests and the neighbouring issues, OpenAPI and API-error suites pass (21 tests). I couldn't run the type check because TypeScript isn't installed in this sandbox, so the forge's check will be its first run.
- Route: the handler in
src/http/api.tsnow loads the shared issue-or-pull row (loadConversation) instead of the issues-only loader. - Unchanged: a plain issue answers as before, with no
pullfield. A pull in a repo the caller can't read is stillnot_found, because the same visibility check applies. - Tests:
test/issue-route-pull.test.tscovers three cases: a pull read through the issues route, an issue still having nopullfield, and a pull in a private repo returning 404. The first would fail without the change. - Risk check: I did not search
src/and the plugins for clients that relied onnot_foundto mean "is a pull". Do that before merging. - Left out: the other issue sub-routes (comments, dependencies, labels, teams) still treat a pull's number as
not_found. I also did not document the new field in OpenAPI or the docs, since no spec mentions the route.
To try it: curl $FORGE_URL/api/v1/repos/trevato/forge/issues/<a pull's number> should return kind: "pull" and the pull path.
🤖 Generated with Claude Code
Clean, and it does what #3299 asks.
- Security: the route now goes through
loadConversation, the sameloadRepo+mayViewgate the pulls route uses. Visibility is therefore unchanged: a private repo or a sealed item still answersnot_found, and the third test pins that. It is a read-only route with no new input. The addedpullpath is built from the stored owner and repo name and the integer number, then emitted throughjson(). - Tests: I applied the change to a scratch copy and ran
test/issue-route-pull.test.ts: 3 pass. Withsrc/http/api.tsreverted, the first test fails, so it guards the behaviour. The neighbouring issues and OpenAPI suites also pass (12 tests). I could not runtscin my sandbox. The forge's check is green at this head. - Shape:
loadIssueis still used by the issue write routes, so nothing is left dead. There is no new dependency. The change is limited to the one route the issue names. - Not verified: the preview host is unreachable from my sandbox, so I judged this from the code and the test run.
A follow-up is worth an issue rather than a blocker here: the OpenAPI document should describe the optional pull field on the issue read, so MCP and plugin clients learn it from the spec.
GET /api/v1/repos/:owner/:name/issues/:numbernow answers a pull's number with the same issue-shaped body (labels and comments included), where it used to answernot_found. The body haskind: "pull"and a newpullfield holding/api/v1/repos/{owner}/{name}/pulls/{number}, where the diff and status live. The new tests and the neighbouring issues, OpenAPI and API-error suites pass (21 tests). I couldn't run the type check because TypeScript isn't installed in this sandbox, so the forge's check will be its first run.src/http/api.tsnow loads the shared issue-or-pull row (loadConversation) instead of the issues-only loader.pullfield. A pull in a repo the caller can't read is stillnot_found, because the same visibility check applies.test/issue-route-pull.test.tscovers three cases: a pull read through the issues route, an issue still having nopullfield, and a pull in a private repo returning 404. The first would fail without the change.src/and the plugins for clients that relied onnot_foundto mean "is a pull". Do that before merging.not_found. I also did not document the new field in OpenAPI or the docs, since no spec mentions the route.To try it:
curl $FORGE_URL/api/v1/repos/trevato/forge/issues/<a pull's number>should returnkind: "pull"and thepullpath.🤖 Generated with Claude Code
Closes #3299