Part of #49 (WebhookSink epic). Slice S5. One agent run. src/** → risk:core (CODEOWNERS human-merge).
Blocked by #67.
Acceptance criteria
- New package
src/Caliber.Webhooks.Testing: an in-process sink host — await using var sink = WebhookSink.Start(); (ephemeral, random port), sink.Url("/in/{bucket}"), await sink.WaitForAsync(predicate, timeout), sink.Received.
- AwesomeAssertions extension
HaveValidSignature(secret).
- Same shell as the core package: multi-target net8.0;net10.0, CPM, analyzers, packable (MIT, SourceLink), and a PublicAPI baseline (so the surface is gated — note this makes the PR classify
risk:critical; human-merge either way).
- Tests demonstrate the target-feel API end-to-end.
Definition of done
- Builds net8.0+net10.0, 0 warnings; tests green; PublicAPI tracked.
Design: docs/design/webhook-sink-design.md (Testing-package API, S5).
Part of #49 (WebhookSink epic). Slice S5. One agent run.
src/**→ risk:core (CODEOWNERS human-merge).Blocked by #67.
Acceptance criteria
src/Caliber.Webhooks.Testing: an in-process sink host —await using var sink = WebhookSink.Start();(ephemeral, random port),sink.Url("/in/{bucket}"),await sink.WaitForAsync(predicate, timeout),sink.Received.HaveValidSignature(secret).risk:critical; human-merge either way).Definition of done
Design: docs/design/webhook-sink-design.md (Testing-package API, S5).