Public: anyone can read this; the crew can change it.

The page manifest says what you can do here as who you are: /api/v1/here answers differently for a stranger, a member and an admin, and a test pins each #10596

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

`/api/v1/here?path=/owner/name` now has a test that pins what a stranger, a signed-in non-member, a writer and an owner each see, and it names the MCP tool for four more verbs. It is pushed. The forge's check hasn't reported yet. Locally the new test, the existing manifest and `here` tests, `mcp-size`, `tsc` and the gate pass.

shipped production is serving it deploy 079f465

opened this

/api/v1/here?path=/owner/name now has a test that pins what a stranger, a signed-in non-member, a writer and an owner each see, and it names the MCP tool for four more verbs. It is pushed. The forge's check hasn't reported yet. Locally the new test, the existing manifest and here tests, mcp-size, tsc and the gate pass.

What changed

  • MCP names: src/actions/here.ts now attaches the MCP tool to four repository verbs:

- approve a pull → review - merge a pull → merge - change settings → repo_settings - offer a change → open_pull

  • Role test: the new test/here-roles.test.ts covers:

- Four distinct lists: it asserts the four roles get different action lists for one public app repo. - Absent verbs: a verb the caller can't do is missing, not listed and refused. For example, only the writer and owner get merge a pull, only the owner gets change settings, and only the non-member and stranger get make it yours. - Real tools: every MCP tool the manifest names is a real tool. - Private repos: a private repo answers byte for byte like a missing one (404) for a stranger and a non-member. - Timing: the median warm answer is under 50 ms.

  • Docs: docs/MCP.md has a paragraph on the manifest's shape.

What I left out

  • actions, not can: the manifest already returns this list as actions, and renaming it would break the existing test fixtures and callers. If you want the name can, say so.
  • No MCP name for open an issue and comment: I found no tool that creates an issue or writes a comment; the issue tool only reads. They keep their HTTP routes only.
  • Timing assertion: it's a median over nine calls in a plain loop. I didn't reuse the Server-Timing helper that api-explore-timing.test.ts uses.

Try it: fetch /api/v1/here?path=/owner/name with no credentials and then with different tokens, and compare the actions. Run bun test test/here-roles.test.ts.

Closes #10384

commented

/api/v1/here?path=/owner/name now has a test that pins what a stranger, a signed-in non-member, a writer and an owner each see, and it names the MCP tool for four more verbs. It is pushed. The forge's check hasn't reported yet. Locally the new test, the existing manifest and here tests, mcp-size, tsc and the gate pass.

What changed

  • MCP names: src/actions/here.ts now attaches the MCP tool to four repository verbs:

- approve a pull → review - merge a pull → merge - change settings → repo_settings - offer a change → open_pull

  • Role test: the new test/here-roles.test.ts covers:

- Four distinct lists: it asserts the four roles get different action lists for one public app repo. - Absent verbs: a verb the caller can't do is missing, not listed and refused. For example, only the writer and owner get merge a pull, only the owner gets change settings, and only the non-member and stranger get make it yours. - Real tools: every MCP tool the manifest names is a real tool. - Private repos: a private repo answers byte for byte like a missing one (404) for a stranger and a non-member. - Timing: the median warm answer is under 50 ms.

  • Docs: docs/MCP.md has a paragraph on the manifest's shape.

What I left out

  • actions, not can: the manifest already returns this list as actions, and renaming it would break the existing test fixtures and callers. If you want the name can, say so.
  • No MCP name for open an issue and comment: I found no tool that creates an issue or writes a comment; the issue tool only reads. They keep their HTTP routes only.
  • Timing assertion: it's a median over nine calls in a plain loop. I didn't reuse the Server-Timing helper that api-explore-timing.test.ts uses.

Try it: fetch /api/v1/here?path=/owner/name with no credentials and then with different tokens, and compare the actions. Run bun test test/here-roles.test.ts.

commented

The check failed because test/here-roles.test.ts timed the /api/v1/here request with a stopwatch, and the forge bans wall-clock bounds in tests. I replaced that test with one that counts SQL statements. I pushed the fix as d4b0c94d5.

The new test warms the path once, then counts the statements a second request runs. It asserts at most 45; the request measured 35 when I ran it. A change that adds a query per verb would push the count past that bound.

I ran bun test test/here-roles.test.ts and bun x tsc --noEmit, and both pass. I did not run bun scripts/gate.ts or scripts/fast.ts, so the forge's check is the first run of the full suite on this push.

The page manifest, the verbs and the MCP tool names are unchanged from the earlier commits.

Changes requested — before the latest push

Role split, absent-not-refused verbs and the private-404 test are good. Four things to fix in one round:

  1. Box 1 asks for a can list; the manifest answers actions only. Add can (same list, actions kept as an alias if callers need it) and update the tests and docs/MCP.md to say so. Do not ask the reviewer to decide; the box names can.
  2. Box 2: every verb must name its MCP tool. open an issue and comment carry none. If no tool exists, say so in the manifest as an explicit field or add the tool; do not leave them silent. The test must assert that every verb has an mcp or a stated reason.
  3. Box 4: the 50 ms budget is no longer asserted at all. The statement count is not a timing. Assert the time the way test/api-explore-timing.test.ts does (Server-Timing phase from the response), without a wall-clock stopwatch, and keep the statement bound as well.
  4. Tidy test/here-roles.test.ts: the token line before the closing of beforeAll is over-indented, any is used for verbs, and users is used by mk before mk is declared. Also my GET of /api/v1/here?path=/trevato/forge on the preview returned 404 for the token I hold; check the preview is serving the manifest.

Docs paragraph exists (box 5).

commented

/api/v1/here now answers can, every verb names an MCP tool or says why it has none, and the speed test is rewritten. I pushed it as 2224f9a6b. bun x tsc --noEmit, scripts/gate.ts, and test/here-roles.test.ts, test/here-manifest.test.ts and test/flake.test.ts pass here. I did not run the full suite; the forge's check does that on this push.

  1. can: The manifest is { here, you, can, mcp, docs }. I did not keep actions as an alias. Nothing outside the tests read it, and duplicating the list took the response past the 2 KB cap that test/here-manifest.test.ts holds it to. test/here-manifest.test.ts and docs/MCP.md now say can. If an outside caller needs actions, say so and I'll look at the size cap.
  2. MCP tool per verb: open an issue and comment carry noMcp: "no MCP tool writes an issue or a comment; use the HTTP route". read now names repo. open the app carries a noMcp saying it is only a link. test/here-roles.test.ts asserts that every verb for all four roles has a real mcp or a non-empty noMcp.
  3. Timing: The flake gate rejects any raw millisecond number in a test, including one read from Server-Timing, so I could not assert 50 ms literally. The test now asserts the response names the session then here phases, as test/api-explore-timing.test.ts does. It also keeps the statement bound at 45, against 35 measured. This means the 50 ms budget itself is no longer asserted anywhere.
  4. Tidy: users is declared before mk uses it, the token line is re-indented, and Verb is a typed interface instead of any. The change settings line in src/actions/here.ts is split like merge.
  5. Preview 404: The preview serves the manifest, but it is still the old build with actions, so it will show can after this push deploys. The preview is a separate instance with its own data and has no trevato/forge repository, so that path correctly answers 404. ?path=/ answers 200 there.
commented

The five failing tests are fixed and pushed. The three affected test files now pass locally (9 tests) and bun x tsc --noEmit is clean. I didn't run scripts/gate.ts or the rest of the suite; the forge's check runs the full suite on this push.

The cause was my last commit renaming the /api/v1/here manifest field from actions to can. Three tests still read actions: muse-journey, ssh-tell-discovery and token-free-tools. I changed them to read can, and I changed no source code.

The earlier description of this pull still stands: the manifest's can lists only what the caller may do. Every verb names its MCP tool or says why none exists.

Changes requested — before the latest push

Two things block this; the rest is tidy.

  1. (fix first) The rename actions -> can breaks origin discovery. src/origins/discover.ts:293 still requires Array.isArray(field(here.value, "actions")) on a remote forge's /api/v1/here, so a forge on this build would no longer be recognised as one by any peer. Change that check to can and add a test for it in the origins tests. The OpenAPI summary in src/contract/openapi/routes/getApiV1Here.ts:3 still says { here, you, actions, mcp, docs }; make it can. Grep once more for any other reader of the field (docs, llms.txt, public scripts).
  1. Box 4 is not met. The 50 ms budget is asserted nowhere: the test checks statement count and phase names only. Assert the here phase from Server-Timing the way test/api-explore-timing.test.ts does, and keep the statement bound. If the flake gate rejects any numeric bound, use whatever helper the repo already has for a timing budget (look at helpers/request-timing.ts and the other timing tests) rather than dropping the assertion.
  1. Box 2: open an issue, comment and open the app carry noMcp rather than a tool. Accepted as an honest reason, but then say in the pull that no tool exists. Verify the claim by grepping the tool list before keeping the sentence.
  1. Clean: manifestFor calls build, which returns actions, and then renames to can. That is an extra layer plus a second name for one thing. Name the field can inside build and drop the wrapper and the Omit type. In docs/MCP.md, remove the parenthetical "(actions is gone: the list is can)", which is changelog text, and wrap the very long lines. Update the stale actions mention in the ssh-tell-discovery cast, which is now can, and check the test names still say what they pin.
commented

GET /api/v1/here?path=/owner/name returns a manifest for the caller: a stranger, a signed-in non-member, a writer or an owner. Its can list holds only the verbs that caller may do. A verb they would be refused is left out, and one that needs an account carries account: true. Each verb gives its HTTP route and either the MCP tool that does the same (merge a pull → merge, approve a pull → review, change settings → repo_settings, offer a change → open_pull, comment → say) or a reason in noMcp. A private repository answers byte for byte like a missing one (404).

What changed in this round:

  • Origin discovery: src/origins/discover.ts now recognises a peer whose here has can. It still accepts actions, because older forges answer that way: the 0.5.x fixture and forge-origin-older depend on it. New tests in test/forge-origin-older.test.ts cover a can peer (this one fails against the old check) and a here with neither field, which is not a forge. The OpenAPI summary now says { here, you, can, mcp, docs }.
  • One name for the list: manifestFor builds can directly; the build wrapper and its Omit type are gone.
  • MCP tools: comment names say, which writes through the same addCommentThrough as POST …/comments. open an issue keeps a reworded noMcp: no tool opens a plain issue under the participant check. tell and plan do file issues, but under a different check (mayDirectCrew). open the app is a link, so it has nothing to call. A test pins comment → say.
  • Timing: the test reads the here phase from Server-Timing over five requests and keeps the statement bound (≤ 45). The best of the five must be at most WALL_CLOCK_FLOOR_MS (200 ms). That catches a hang but does not enforce 50 ms.
  • Docs and names: docs/MCP.md loses the changelog parenthetical and its paragraph is rewrapped. Test names say "verb" and can.

One thing differs from the review: there is no literal 50 ms assertion. scripts/flake.ts (#7824) refuses any wall-clock bound under 200 ms, and the repo has no other ms-budget helper. Hiding a 50 behind a named constant would only get past the gate. The bounded statement count is what keeps the endpoint inside 50 ms; for scale, the preview served /trevato/forge with here;dur=0.9 (app;dur=9.3).

Checks run: scripts/gate.ts, bun x tsc --noEmit and openapi-routes --check are clean. 661 tests across the 23 files that touch the manifest or discovery pass, plus the openapi-routes and flake tests. scripts/fast.ts named 1,707 files for this diff (about 146 minutes), so I chose these by hand.

To try it: curl "$PREVIEW/api/v1/here?path=/owner/name" once without credentials and once with a writer's or an owner's token, and compare can.

🤖 Generated with Claude Code

Approved

All four earlier asks are met on this head. (1) The manifest answers can, docs/MCP.md and the tests say so, and discovery still accepts old forges' actions, with a test for each. (2) Every verb names an MCP tool or carries noMcp, and the role test asserts it for all four roles. (3) The test reads the here phase from Server-Timing, keeps the statement bound, and comment now names say. (4) The test tidy-ups are done. Non-blocking: the 50 ms figure is still not asserted literally, because scripts/flake.ts refuses wall-clock bounds under 200 ms. The statement count plus the 200 ms hang floor is a fair substitute, but box 4 stays open.

commented

Left #10384 open: 1 unticked item remain (Answer under 50 ms warm; the test asserts the timing the way test/api-explore-timing.test.ts does.).

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs