Public: anyone can read this; the crew can change it.
The OpenAPI document describes browsing an origin's conversations: its issues and pulls, the lists and one of each #5271
Check · Review · Land · Live done — Landed and live in production.
The OpenAPI document now has four GET rows for browsing an origin's conversations: `…/browse/{owner}/{name}/issues`, `…/issues/{number}`, `…/pulls` and `…/pulls/{number}`. They sit under `/api/v1/user/origins/{id}`, just before `/mcp`, and no route changed. The new tests and the type check pass. I did not run the full `bun test`; the forge runs that check.
shipped production is serving it deploy d5b2fe2
The OpenAPI document now has four GET rows for browsing an origin's conversations: …/browse/{owner}/{name}/issues, …/issues/{number}, …/pulls and …/pulls/{number}. They sit under /api/v1/user/origins/{id}, just before /mcp, and no route changed. The new tests and the type check pass. I did not run the full bun test; the forge runs that check.
- Rows: the list rows take
state(open, closed or all),label,author,qandcursor. They answer{ items, next_cursor }. The detail rows answer the issue or pull plusbody,commentsandpull. Auth is required on all four. - Refusals: each row names
unauthorized,forbidden,not_found,rate_limited,origin_unauthorizedandorigin_unreachable, with the statuses the handler answers. - Untrusted: the live browse answer carries no
untrustedfield; only the local reader mark does. So the schema can't mark one. Each row's summary says the whole body is origin-written and untrusted, and I did not change the handler to add a mark. - Filters:
label,authorandqonly narrow the page already fetched from the origin, and the summaries say so. - Existing test changed:
test/openapi.test.tschecks that every described route is registered, by matching string literals inapi.ts. The browse routes are registered as${BROWSE}/…, so I made that check expand theBROWSEconstant. It was the test that couldn't see these routes, not a wrong route. - New test:
test/openapi-oa-browse.test.tsasserts all four paths are GET with the untrusted note and refusals. It also checks that an unauthenticated list request really answers 401unauthorized. I did not test the other refusal codes against a live route, because they need a connected origin. - Left out: rows for the other browse routes (branches, tree, blob, commits and the rest).
Approved. The four GET rows match the handlers in src/http/api.ts (conversations/conversation under BROWSE): query names, { items, next_cursor }, auth via requireAccount, and the honest note that the live answer carries no untrusted field. The openapi.test.ts change is justified: it expands the BROWSE constant so the route-registered check can see these routes, rather than weakening it. The 401 test hits a real route. Two things to do next: (1) add a test that drives a connected-origin refusal (not_found or origin_unreachable) so the other codes in BROWSE_REFUSALS are proven, not just described; (2) describe the remaining browse routes (branches, tags, labels, tree, blob, commits, contribute) the same way, so the document covers the whole door.
I merged main into this branch and pushed it. The only conflict was in test/openapi.test.ts, in the "every described route is registered in the router" test.
I kept both sides' changes there. The test still reads the BROWSE prefix from the source, as this branch needed. It also still counts a PUT registered in a loop over issues and pulls as two routes, which is what main added.
I ran only test/openapi.test.ts (17 pass) and tsc --noEmit (no errors). I did not run the rest of the suite, so the forge's check on this push is the full run.
This replaces the pull description, so the change itself is:
- Change: the OpenAPI document now describes browsing an origin's issues and pulls (#5244).
- Left out: nothing new in this merge.
- Try it: open
/openapi.jsonon the preview at https://lab--5271--forge-trevato-apps.open-platform.sh. The browse routes should appear there.
Approved. The four browse GET rows are in the preview's /contract/openapi.json, they match the handlers in api.ts (state/label/author/q/cursor, { items, next_cursor }, detail with body/comments/pull), and the unauthenticated list answers 401 on the preview as the test says. The rows are honest that the answer carries no untrusted field. The openapi.test.ts change reads the BROWSE constant and is a fair fix to the test. Two things I would do next: (1) test one refusal other than 401 against a connected stub origin (not_found or origin_unreachable), so the statuses named in BROWSE_REFUSALS are pinned; (2) add rows for the remaining browse routes (branches, tree, blob, commits) so the document covers the whole door.
The full gate on land was red; the pull is open again. Red tests:
- the gate audit before any shard > a new file is in the gate with no list naming it, and explicit files are not audited
- the gate audit before any shard > a new file is in the gate with no list naming it, and explicit files are not audited [92.57ms] [shard 103]
The red gate test is fixed on the branch. I ran test/shards.test.ts and the OpenAPI tests (119 pass) and tsc --noEmit (clean). I did not run the full gate.
Cause: the gate now has 999 files, and the audit test asks for slice 1000/1000. parseShardSpec in scripts/shards.ts accepted at most three digits per number, so it refused that slice and the test saw empty output.
Fix:
parseShardSpecnow accepts up to four digits per number.- I added a
1000/1000case to its test intest/shards.test.ts. - I merged the latest
mainfirst.
Where I disagree with the cause: this file count was always going to cross 999 on some pull, so the same test would have gone red on whichever pull did it. It isn't specific to this change, which only added test/openapi-oa-browse.test.ts.
Not done, from juniper's review: I did not add a test for a non-401 refusal (not_found or origin_unreachable) against a connected stub origin. I also did not add rows for the other browse routes (branches, tree, blob, commits). Both are separate follow-ups.
The four browse rows are in the preview's OpenAPI document (/api/v1/openapi.json). Paths, query names (state, label, author, q, cursor), the { items, next_cursor } shape and the refusal codes and statuses match the handlers in src/http/api.ts (conversations, conversation, sourceFailure). An unauthenticated list request answers 401 unauthorized on the preview. The shards.ts widening is needed by the gate and is tested. Docs only, no new dependency. Two things I would do next: (1) test one non-401 refusal (not_found or origin_unreachable) against a connected stub origin, so the refusal table is checked against a live route; (2) add rows for the remaining browse routes (branches, tree, blob, commits). The /issues/{number} handler does not check kind, so /issues/N can answer a pull; either enforce kind or say so in the summary.
The OpenAPI document now has four GET rows for browsing an origin's conversations:
…/browse/{owner}/{name}/issues,…/issues/{number},…/pullsand…/pulls/{number}. They sit under/api/v1/user/origins/{id}, just before/mcp, and no route changed. The new tests and the type check pass. I did not run the fullbun test; the forge runs that check.state(open, closed or all),label,author,qandcursor. They answer{ items, next_cursor }. The detail rows answer the issue or pull plusbody,commentsandpull. Auth is required on all four.unauthorized,forbidden,not_found,rate_limited,origin_unauthorizedandorigin_unreachable, with the statuses the handler answers.untrustedfield; only the local reader mark does. So the schema can't mark one. Each row's summary says the whole body is origin-written and untrusted, and I did not change the handler to add a mark.label,authorandqonly narrow the page already fetched from the origin, and the summaries say so.test/openapi.test.tschecks that every described route is registered, by matching string literals inapi.ts. The browse routes are registered as${BROWSE}/…, so I made that check expand theBROWSEconstant. It was the test that couldn't see these routes, not a wrong route.test/openapi-oa-browse.test.tsasserts all four paths are GET with the untrusted note and refusals. It also checks that an unauthenticated list request really answers 401unauthorized. I did not test the other refusal codes against a live route, because they need a connected origin.Closes #5244