Graceful shutdown

Buffered outlets hold payloads that have not yet reached their destination. On a clean shutdown you want those drained, the background loops stopped, and any held resources released, all within a deadline.
The Manifold exposes three lifecycle methods:
func (m *Manifold) Flush(ctx context.Context) error // push buffered outlets out nowfunc (m *Manifold) Shutdown(ctx context.Context) error // flush, then stop + drainfunc (m *Manifold) Close() error // io.Closer: Shutdown(context.Background())FlushcallsFlushon every attachedFlusher(aReservoir, thestatsdaggregator) and joins their errors. Use it to force a release without tearing anything down, for example before reading back what landed in a test.Shutdownflushes first, then callsShutdownon everyShutdowner, draining in-flight work withinctx’s deadline. Background loops stop and wait to exit (no goroutine leak); shutdown is idempotent.Closeis theio.Closerconvenience:Shutdownwith a background context, fordefer-friendly call sites.
A typical server wires it to its shutdown signal with a bounded deadline:
m := sink.NewManifold(sink.WithMeter(meter)).Attach( sink.Reservoir(s3Outlet, sink.WithBatchInterval(5*time.Second)),)// ... serve ...
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)defer cancel()if err := m.Shutdown(shutdownCtx); err != nil { log.Error("sink drain incomplete", "error", err)}A Poller is driven separately: Stop cancels its loop and waits for the
in-flight collection to finish.
p := sink.NewPoller(target, collect).Start(ctx)defer p.Stop()Because draining is bounded by the context you pass, a wedged destination can never hang your shutdown forever. It fails the deadline, the error is joined and returned, and your process exits. Thin seams, predictable teardown.