~bigbes/sr-ht-spec: d8a5164f

feat(webhooks): fire on proposal open/merge/reject (Phase 5a)

The firing half — proposal lifecycle events now deliver GraphQL-native
webhooks. Verified end to end against a live daemon: an agent REST
propose delivers a signed POST whose body is the subscription's stored
query executed against the ProposalEvent payload.

- service: an EventSink seam (service/events.go). Propose emits
  PROPOSAL_OPENED for a new proposal, mergeProposal emits PROPOSAL_MERGED
  (the single merge point — both auto-merge and the human approve reach
  it), Reject emits PROPOSAL_REJECTED. Nil-safe; a Service with no sink
  emits nothing.
- graph.NewProposalEvent builds the *model.ProposalEvent payload from a
  service.Proposal (reusing the existing service→graph→model mapping).
- cmd webhookEventSink: proposal events happen in the service layer,
  which has none of core-go's request context, so the sink enqueues a
  dowork task onto the webhook queue. The task runs in the queue's worker
  context (server+database+config, from WithQueues), adds the owner's
  INTERNAL auth, and calls Schedule — which renders each subscriber's
  query and delivers it Ed25519-signed. Fire-and-forget off the write
  path: a webhook never blocks or fails a proposal write.

Phase 5a (webhooks) is complete: DB, the authn→AuthContext bridge, the
GraphQL surface, the core-go server wiring, and firing.

Eugene Blikh <bigbes@gmail.com> — 2026-07-24 09:49:46 UTC

Commit d8a5164f5f1c6ac49b056eb0cb1b9c3d9db338fdview raw patch

Parent(s): 021d955a

9 changed file(s)

FileStatus+
cmd/specsrht/main.go M +6
cmd/specsrht/webhooks.go A +59
go.mod M +1 -1
graph/webhook_event.go A +45
service/events.go A +18
service/events_test.go A +200
service/merge.go M +14 -2
service/propose.go M +8
service/service.go M +15