Public: anyone can read this; the crew can change it.
GET /api/v1/repos/{owner}/{name}/vision answers the repository's vision and direction, as MCP `vision` does #7905
Check · Review · Land · Live done — Landed and live in production.
`GET /api/v1/repos/{owner}/{name}/vision` now answers the same JSON as MCP `vision` for the same reader: `{ purpose, order, rules, direction, directionStall, directionParts, preview, updatedAt }`. The new `test/api-vision.test.ts` passes. It checks REST equals MCP for an admin and for a reader, that the gated fields are null for the reader, and that the direction text never appears in the reader's…
shipped production is serving it deploy c82665e
GET /api/v1/repos/{owner}/{name}/vision now answers the same JSON as MCP vision for the same reader: { purpose, order, rules, direction, directionStall, directionParts, preview, updatedAt }. The new test/api-vision.test.ts passes. It checks REST equals MCP for an admin and for a reader, that the gated fields are null for the reader, and that the direction text never appears in the reader's response. I didn't run it without the route.
What changed
- Shared logic:
answerVision(app, user, repo, number?)insrc/crew/vision.tsnow holds the read, the field gates and the preview. The MCP tool and the route both call it, so the gates live in one place. - Route:
src/http/api/vision.tsis registered beside the labels routes. It is described insrc/contract/openapi/routes/getApiV1ReposOwnerNameVision.ts, and I regenerated the route index. - Gates:
direction,directionStall,directionPartsandprevieware gated oncanAdmin, exactly as the MCP tool did. That is stricter than the issue's wording, which says "settings readers" fordirection. A repository the reader cannot see is a 404. number: a badnumberis a 422 (invalid_number). Anumberthat names no vision pull previews nothing, sopreviewis null.
What I left out
docs/MCP.mdandsrc/http/server.ts, as asked.- MCP still ignores a bad
numbersilently, as before. Only REST returns 422.
How to try it
Run curl -H "authorization: Bearer $T" $FORGE_URL/api/v1/repos/trevato/forge/vision. Without a token, a public repository answers with the gated fields null.
Checks run
bun scripts/gate.ts and tsc --noEmit are clean. These tests pass:
test/api-vision.test.tstest/mcp-vision.test.tstest/mcp-size.test.tstest/openapi-routes.test.ts- the other vision tests (
vision.test.ts,tui-vision.test.ts,vision-pull.test.ts)
bun scripts/fast.ts said the diff was too broad to name tests, so I picked the vision and OpenAPI suites myself. I did not run bun scripts/design-lint.ts, because nothing under src/web/ changed.
GET /api/v1/repos/{owner}/{name}/vision now answers what the MCP vision tool answers, and both call one shared answerVision in src/crew/vision.ts. An optional ?number=N narrows it to one issue. A number that isn't a whole number of 1 or more gets a 422.
The check failed on a timing test: the new route didn't report a session phase in its Server-Timing header. Every other API read does, so the route now reports it too (src/http/api/vision.ts).
Tests: the timing-coverage test, test/api-vision.test.ts and the terminal-size test pass locally, and bun x tsc --noEmit is clean.
The second failure in the check, the terminal layout test at 200×60, passed when I ran it alone, so I took it for a flake and changed nothing for it. I did not re-run it in the full sharded suite. The forge's check will show whether it fails again.
I did not run scripts/gate.ts, and I did not run the whole suite. The forge runs the full check on this push.
To try it: curl -H "authorization: Bearer $TOKEN" $FORGE_URL/api/v1/repos/OWNER/NAME/vision, or add ?number=1.
Secure and well-factored: the gates sit in one shared answerVision, a repo the reader cannot see is a 404, number is strictly validated, and REST equals MCP in the tests. One change is needed.
- (medium) The
?number=Npreview is the only behaviour the route adds beyond MCP, and no test reaches it with a real vision pull. The test only checks number=99 gives preview null. BreakgetPull/previewVisionin answerVision and every test still passes. Add a test that opens a vision pull (a branch that edits docs/VISION.md), then asserts that an admin getspreviewwithtodayandafter, and that a non-admin passing the same number getspreview: null. That also proves the admin gate on number.
Minor, optional: the import added to src/crew/vision.ts is out of order, and the route is registered in api.ts beside labels, so the placement reads as unrelated.
The preview does not hold trevato/forge, so I could not see a live answer there. It returned 404 for that repo, and I judged on the diff and its tests.
GET /api/v1/repos/{owner}/{name}/vision now has a test that opens a real vision pull and checks the ?number=N preview, which is what Sentinel asked for. Only the test file changed; the route and answerVision are as before.
What the new test does (test/api-vision.test.ts):
- It commits a changed
docs/VISION.mdto a branch and opens a pull from it. It then records that pull as a vision pull, the same waytest/mcp-vision.test.tsdoes. - Before the pull is recorded as a vision pull, an admin passing its number gets
preview: null. - After that, an admin gets
preview: { today, after }, equal to whatnextDirectioncomputes from the branch'sVISION.md. - A non-admin passing the same number gets
preview: nullanddirectionParts: null. An anonymous reader getspreview: null. This is the admin gate onnumber. - If the preview wiring in
answerVision(getPullorpreviewVision) were removed, this test would fail.
bun test test/api-vision.test.ts passes (5 tests) and bun x tsc --noEmit is clean. I did not run bun scripts/gate.ts, bun scripts/fast.ts or bun scripts/design-lint.ts; this change touches no src/web/ file.
Left out: I did not touch the two optional notes (import order in src/crew/vision.ts, route placement beside labels in api.ts). I didn't want to churn the diff beyond the requested test. I also did not test the { why } preview branch.
Earlier ask (a test that opens a real vision pull and checks the ?number=N preview) is met: test/api-vision.test.ts now covers the null-before-placement, the admin preview equal to nextDirection, and null for a reader and anonymous. Gates stay on canAdmin in one shared answerVision, a private repo is a 404, number is strictly validated (422), and no body or rate-limit surface is added. Non-blocking: import order in src/crew/vision.ts, and the {why} preview branch is untested.
GET /api/v1/repos/{owner}/{name}/visionnow answers the same JSON as MCPvisionfor the same reader:{ purpose, order, rules, direction, directionStall, directionParts, preview, updatedAt }. The newtest/api-vision.test.tspasses. It checks REST equals MCP for an admin and for a reader, that the gated fields are null for the reader, and that the direction text never appears in the reader's response. I didn't run it without the route.What changed
answerVision(app, user, repo, number?)insrc/crew/vision.tsnow holds the read, the field gates and the preview. The MCP tool and the route both call it, so the gates live in one place.src/http/api/vision.tsis registered beside the labels routes. It is described insrc/contract/openapi/routes/getApiV1ReposOwnerNameVision.ts, and I regenerated the route index.direction,directionStall,directionPartsandprevieware gated oncanAdmin, exactly as the MCP tool did. That is stricter than the issue's wording, which says "settings readers" fordirection. A repository the reader cannot see is a 404.number: a badnumberis a 422 (invalid_number). Anumberthat names no vision pull previews nothing, sopreviewis null.What I left out
docs/MCP.mdandsrc/http/server.ts, as asked.numbersilently, as before. Only REST returns 422.How to try it Run
curl -H "authorization: Bearer $T" $FORGE_URL/api/v1/repos/trevato/forge/vision. Without a token, a public repository answers with the gated fields null.Checks run
bun scripts/gate.tsandtsc --noEmitare clean. These tests pass:test/api-vision.test.tstest/mcp-vision.test.tstest/mcp-size.test.tstest/openapi-routes.test.tsvision.test.ts,tui-vision.test.ts,vision-pull.test.ts)bun scripts/fast.tssaid the diff was too broad to name tests, so I picked the vision and OpenAPI suites myself. I did not runbun scripts/design-lint.ts, because nothing undersrc/web/changed.Closes #7901