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

The API and MCP `hooks` redeliver a failed webhook delivery #10120

Merged opened this

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

Merged

The API and MCP `hooks` can now redeliver a failed webhook delivery, through one shared action that the Settings form also uses. `bun x tsc --noEmit` is clean. The new test and the existing webhook redeliver tests pass, as do `test/mcp-size.test.ts` and `test/mcp-hooks.test.ts`. I did not run the whole suite, since the forge runs it on push. `bun scripts/gate.ts` printed nothing, so I can't say it…

shipped production is serving it deploy aa46b8b

opened this

The API and MCP hooks can now redeliver a failed webhook delivery, through one shared action that the Settings form also uses. bun x tsc --noEmit is clean. The new test and the existing webhook redeliver tests pass, as do test/mcp-size.test.ts and test/mcp-hooks.test.ts. I did not run the whole suite, since the forge runs it on push. bun scripts/gate.ts printed nothing, so I can't say it passed.

What changed

  • Shared action: redeliverWebhook in src/actions/repos.ts checks that the caller administers the repository and that the hook belongs to it, then calls redeliver. Only a failed row moves, and a sealed report stays refused. webhookRedeliver in src/web/pages/hooks.ts now calls it instead of redeliver directly.
  • API: POST /api/v1/repos/:owner/:name/hooks/:id/deliveries/:delivery/redeliver needs an admin-scope token on a repository you administer. It answers { id, state: "pending" } and gives a 404 to a non-admin, a short-scope token, or an unknown hook or delivery. A row that isn't failed, or a sealed report's, gets a 409 with a sentence. It is added to the OpenAPI routes as postApiV1ReposOwnerNameHooksIdDeliveriesDeliveryRedeliver.ts.
  • MCP: hooks gains action: redeliver with id and delivery, and a new delivery argument capped at 64 characters. The not-failed and sealed cases return a conflict error, and a non-admin or an unknown hook or delivery gets not_found. No new tool was added and no ceilings were raised.
  • Docs and test: docs/features/webhook-redeliver.md now describes both doors in place of "There is no MCP action". test/hook-redeliver-doors.test.ts fails a delivery, redelivers it through each door and reads it pending. It then redelivers the same row again and gets the 409 and the MCP conflict error. It also checks that a stranger, a short scope and an unknown row all get 404 and leave the row failed.

Not done: the new test has no case for a sealed report. The refusal is still covered by the existing core test test/webhook-redeliver.test.ts, which I did not change.

To try it: POST /api/v1/repos/:owner/:name/hooks/:id/deliveries/:delivery/redeliver with an admin token, or MCP hooks { repo, action: "redeliver", id, delivery }. Then read …/hooks/:id/deliveries and the row shows pending.

Closes #10105

commented

The API and MCP hooks can now redeliver a failed webhook delivery, through one shared action that the Settings form also uses. bun x tsc --noEmit is clean. The new test and the existing webhook redeliver tests pass, as do test/mcp-size.test.ts and test/mcp-hooks.test.ts. I did not run the whole suite, since the forge runs it on push. bun scripts/gate.ts printed nothing, so I can't say it passed.

What changed

  • Shared action: redeliverWebhook in src/actions/repos.ts checks that the caller administers the repository and that the hook belongs to it, then calls redeliver. Only a failed row moves, and a sealed report stays refused. webhookRedeliver in src/web/pages/hooks.ts now calls it instead of redeliver directly.
  • API: POST /api/v1/repos/:owner/:name/hooks/:id/deliveries/:delivery/redeliver needs an admin-scope token on a repository you administer. It answers { id, state: "pending" } and gives a 404 to a non-admin, a short-scope token, or an unknown hook or delivery. A row that isn't failed, or a sealed report's, gets a 409 with a sentence. It is added to the OpenAPI routes as postApiV1ReposOwnerNameHooksIdDeliveriesDeliveryRedeliver.ts.
  • MCP: hooks gains action: redeliver with id and delivery, and a new delivery argument capped at 64 characters. The not-failed and sealed cases return a conflict error, and a non-admin or an unknown hook or delivery gets not_found. No new tool was added and no ceilings were raised.
  • Docs and test: docs/features/webhook-redeliver.md now describes both doors in place of "There is no MCP action". test/hook-redeliver-doors.test.ts fails a delivery, redelivers it through each door and reads it pending. It then redelivers the same row again and gets the 409 and the MCP conflict error. It also checks that a stranger, a short scope and an unknown row all get 404 and leave the row failed.

Not done: the new test has no case for a sealed report. The refusal is still covered by the existing core test test/webhook-redeliver.test.ts, which I did not change.

To try it: POST /api/v1/repos/:owner/:name/hooks/:id/deliveries/:delivery/redeliver with an admin token, or MCP hooks { repo, action: "redeliver", id, delivery }. Then read …/hooks/:id/deliveries and the row shows pending.

commented

The gate failed only because no commit message had a "Try:" line. I added an empty commit with one and pushed it. Locally bun scripts/gate.ts now prints nothing, and the forge's check on the new push hasn't reported yet. The code and tests are unchanged from the first push.

What changed: The API and MCP hooks can now redeliver a failed webhook delivery. Both go through one redeliverWebhook action in src/actions/repos.ts, which the Settings form also uses.

  • API: POST /api/v1/repos/:owner/:name/hooks/:id/deliveries/:delivery/redeliver needs an admin-scope token on a repository you administer. It answers { id, state: "pending" }. A non-admin, a short-scope token, or an unknown hook or delivery gets a 404. A row that isn't failed, or a sealed report's, gets a 409 with a sentence and doesn't move. It is in the OpenAPI routes.
  • MCP: hooks gains action: redeliver with id and delivery, with the same checks. The not-failed and sealed cases return a conflict error. No new tool was added and no ceilings were raised.
  • Docs and test: docs/features/webhook-redeliver.md now describes both doors. test/hook-redeliver-doors.test.ts fails a delivery, redelivers it through each door and reads it pending. It also checks that a second redeliver is refused and that strangers and unknown rows get 404. It has no sealed-report case. The existing test/webhook-redeliver.test.ts covers that refusal in the core code.

Try it: On the #10120 preview, POST to the route above with an admin token on a failed delivery, or call MCP hooks { repo, action: "redeliver", id, delivery }. Then GET …/hooks/:id/deliveries shows the row as pending.

commented

The check failed because the generated OpenAPI route index was out of date. I regenerated it with bun scripts/openapi-routes.ts and pushed the result (2e01c326). The only file that changed is src/contract/openapi/routes/index.ts.

  • Tests: the routes test file now passes (368 tests, 0 failures) and bun x tsc --noEmit is clean.
  • Not run: I did not run bun scripts/gate.ts or the other test files this change reaches. scripts/fast.ts reported the diff as too broad (1802 of 2340 files, over its 5-minute budget), so I left the rest to the forge's check.

No behaviour changed in this push. The pull's earlier description still applies: API and MCP hooks redeliver a failed webhook delivery through one action. To try it, use the preview at https://lab--10120--forge-trevato-apps.open-platform.sh.

Changes requested — before the latest push

The code reads clean: one shared action, admin-scope check mapped to 404, bearer-token API with no CSRF surface, id lengths capped in MCP, OpenAPI route and feature doc updated, no README change. One change needed: the new 409 sealed branch in both the API and MCP handlers has no test in this pull. Add a sealed-report delivery case to test/hook-redeliver-doors.test.ts that asserts 409 on the API and conflict on MCP, and that the row stays failed. Optional: also assert an MCP call with an rw-scope token is refused.

commented

I added the sealed-report test sentinel asked for, plus the optional rw-scope check. Both pass, along with tsc --noEmit and scripts/gate.ts. I ran only test/hook-redeliver-doors.test.ts, not the full suite. No source code changed.

The new test is in test/hook-redeliver-doors.test.ts. It fails a delivery, then seals the issue behind it. This is the sequence for the test:

  • API: redelivery returns 409 and the message mentions "sealed".
  • MCP: the call is refused with an error that mentions "sealed".
  • Row state: the row is still failed after each attempt.
  • rw-scope token on MCP: the call is refused and the row stays failed.

Setup: the webhook fan-out never queues a delivery for an event that is already sealed. The test therefore creates the delivery while the issue is still open and seals the issue afterwards.

Helper change: a scope refusal on MCP comes back as a JSON-RPC error rather than a tool result. The test's mcp helper now handles that shape.

Approved

The earlier ask is met. The sealed-report case is now tested on both doors, and the rw-scope MCP refusal is checked too. Nothing else changed in the source. The Settings form now goes through the same admin-checked action and the shared action keeps the 404/409 split. Docs are in docs/features, README untouched.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs