sink/bridge
import "github.com/stablekernel/crucible/sink/bridge"Package bridge composes a crucible/state machine with a crucible/sink Manifold without either core importing the other. It adapts state’s two non-required observation seams to a sink fan-out:
- Middleware wraps a machine’s Fire so every successful transition fans out through a Manifold. Because Fire carries a context.Context, the middleware starts a “state.transition” span and propagates its context into Manifold.Sink, so the “sink.Sink” span (and each outlet’s downstream span) nests under the transition span through the shared crucible/telemetry tracer. This is the context-propagating, trace-correlating path.
- Inspector adapts a Manifold to state’s Inspector observer. It is the ergonomic one-liner for “fan every transition out”, but state.Inspector carries no context.Context, so it cannot propagate trace context; use Middleware when span nesting matters.
Stability
Section titled “Stability”Experimental (pre-v1); the API may change until the suite locks v1.0.0.
- func Inspector(m *csink.Manifold) state.InspectorFunc
- func Middleware[S comparable, E comparable, C any](m *csink.Manifold, opts …Option) state.Middleware[S, E, C]
- type Option
- type Transition
func Inspector
Section titled “func Inspector”func Inspector(m *csink.Manifold) state.InspectorFuncInspector adapts m to a state.Inspector, fanning each transition event out through m. It uses context.Background because state.Inspector carries no context, so emit spans do not nest under a transition span; use Middleware for trace correlation. Register it with the WithInspector cast option.
func Middleware
Section titled “func Middleware”func Middleware[S comparable, E comparable, C any](m *csink.Manifold, opts ...Option) state.Middleware[S, E, C]Middleware returns a state.Middleware that fans every successful transition out through m. It starts a transition span on the configured tracer and propagates that span’s context into m.Sink, so the emit span nests under the transition span. Install it with the machine builder’s Use method.
Example
bucket := csink.NewBucket()m := csink.NewManifold(csink.WithOutlets(bucket))
// Install the bridge as middleware: every transition fans out through m.machine := state.Forge[string, string, *bulb]("switch"). Use(bridge.Middleware[string, string, *bulb](m)). State("off").State("on"). Initial("off"). CurrentStateFn(func(b *bulb) string { return b.cur() }). Transition("off").On("toggle").GoTo("on"). Quench(state.Strict())
machine.Cast(&bulb{}).Fire(context.Background(), "toggle")
tr := csink.RecordsOf[bridge.Transition](bucket)[0]fmt.Printf("%s: %s -> %s\n", tr.Event, tr.From, tr.To)// Output: toggle: off -> onOutput
Section titled “Output”toggle: off -> ontype Option
Section titled “type Option”Option configures the bridge adapters.
type Option func(*config)func WithSpanName
Section titled “func WithSpanName”func WithSpanName(name string) OptionWithSpanName overrides the transition span name (default “state.transition”).
func WithTracer
Section titled “func WithTracer”func WithTracer(t telemetry.Tracer) OptionWithTracer sets the tracer the Middleware starts the transition span on. The default is telemetry.NopTracer(). Wire the same tracer the Manifold uses so the emit span nests under the transition span. A nil tracer is ignored.
type Transition
Section titled “type Transition”Transition is the payload the bridge fans out for each observed state transition. Register a transformer for it on each destination’s registry to persist or publish transitions.
type Transition struct { // Machine is the name of the machine that transitioned. Machine string // Event is the human-readable label of the event that drove the transition. Event string // From is the primary active leaf before the transition. From string // To is the primary active leaf after the transition. To string}Generated by gomarkdoc