37 lines
1.7 KiB
Markdown
37 lines
1.7 KiB
Markdown
|
|
---
|
||
|
|
brief: 1
|
||
|
|
convoy: owner-cockpit-v02
|
||
|
|
role: role-implementer
|
||
|
|
title: Forge webhook receiver — GitHub/Forgejo → pr_review→merged
|
||
|
|
status: ready
|
||
|
|
priority: P0
|
||
|
|
slice_dependencies: []
|
||
|
|
recommended_model: qwen-coder
|
||
|
|
model_tier: local
|
||
|
|
---
|
||
|
|
|
||
|
|
# Brief 1 — `Task-forge-webhook-receiver.md` (Epic 8)
|
||
|
|
|
||
|
|
## Goal
|
||
|
|
|
||
|
|
`POST /api/webhooks/forge` verifies signature via `FORGE_WEBHOOK_SECRET`, finds `echodo-convoy: <uuid>` marker in PR body (from `apps/worker/src/git/pr.ts`), transitions `pr_review→merged` via `transitionConvoy` `packages/shared/src/convoy-lifecycle/state-machine.ts:90` as `automation`, writes `convoy_events`.
|
||
|
|
|
||
|
|
## Files
|
||
|
|
|
||
|
|
- `apps/web/app/api/webhooks/forge/route.ts` NEW — parse `X-Hub-Signature-256` (GitHub) or `X-Forgejo-Signature` (Forgejo) HMAC sha256 `FORGE_WEBHOOK_SECRET`, handle `pull_request: closed & merged=true` + `pr.body` marker regex `/echodo-convoy:\s*([0-9a-f-]{36})/`, lookup convoy by `id`, validate `status===pr_review`, call `transitionConvoy(db, {workspaceId, convoyId, toStatus:merged, actor:automation, reason: pr#N merged})`. Return 200 + `ok` even if no convoy (don't leak). Rate-limit per IP.
|
||
|
|
- `apps/web/server/lib/forge/adapters.ts` — reuse `ForgeAdapter` if needed for verify.
|
||
|
|
- `docs/operations/live-e2e-checklist.md` — add webhook step.
|
||
|
|
|
||
|
|
## Acceptance
|
||
|
|
|
||
|
|
- Forgejo/GitHub payload with `body: "… echodo-convoy: <id> …"` + valid HMAC transitions `pr_review→merged`; invalid signature 401; no marker 200 no-op.
|
||
|
|
- Existing `pnpm lint && pnpm type-check` pass.
|
||
|
|
|
||
|
|
## Prompt
|
||
|
|
|
||
|
|
You are `role-implementer` brief 1. Read `packages/shared/src/convoy-lifecycle/state-machine.ts`, `apps/web/server/lib/forge/adapters.ts`, `apps/worker/src/git/pr.ts`, `docs/convoy-lifecycle.md`. Keep verify constant-time, workspace-scoped.
|
||
|
|
|
||
|
|
## Depends
|
||
|
|
|
||
|
|
None.
|