Connection lifecycle

The observable health state of every connector and the policy by which it retries after a disconnect. This cluster :satisfies: Connector framework (FEAT_0030).

Feature: Connection lifecycle FEAT_0034
status: open
satisfies: FEAT_0030
is refined by: QG_0004, ADR_0009

The observable health state of every connector and the policy by which a connector retries after a stack-level disconnect. Both surfaces are uniform across protocols, regardless of which protocol stack owns the reconnect mechanism.

Requirement: ConnectorHealth state machine REQ_0230
status: approved
satisfies: FEAT_0034
is refined by: IMPL_0010, ARCH_0012
is verified by: TEST_0101, TEST_0161

The framework shall define ConnectorHealth as an enum with variants Up, Connecting { since }, Degraded { reason }, and Down { reason, since }. Every connector shall report current health via Connector::health().

Requirement: subscribe_health returns a Channel of HealthEvent REQ_0231
status: approved
satisfies: FEAT_0034
is refined by: IMPL_0040

Connector::subscribe_health() shall return an observable handle over the connector’s HealthEvent stream so callers can wire health transitions into ExecutableItem triggers. The handle type is connector-implementation dependent — typically a taktora-executor Channel<HealthEventWire> (where HealthEventWire is the POD wire form, preferred for cross-process gateways) or a thin in-process wrapper around a crossbeam_channel::Receiver<HealthEvent> (acceptable when the plugin and gateway share an address space). The choice is recorded in the connector’s impl:: directive (e.g. taktora-connector-host crate (IMPL_0040)).

Requirement: Health subscriptions are independent broadcast streams REQ_0847
status: implemented
satisfies: FEAT_0034
is verified by: TEST_0864

Every call to Connector::subscribe_health() (and to the underlying health monitor’s subscribe()) shall return an independent stream that observes every health transition emitted after the call. Events shall never be load-balanced between subscriptions, and a transition with zero subscribers shall succeed (observable via Connector::health()). Cloning a subscription handle remains a competing-consumer tap of that one stream and shall be documented as such.

Rationale. The previous implementation handed out clones of a single crossbeam_channel receiver — competing consumers documented as broadcast. Found live on the WAGO bench (issue #60): a fast-polling second subscriber silently stole every event from the health pump, so a real unplug/recover cycle printed no transitions at all while the data plane worked — the observability surface lying by omission.

Requirement: ReconnectPolicy trait REQ_0232
status: open
satisfies: FEAT_0034
is refined by: IMPL_0010
is verified by: TEST_0137

The framework shall define a ReconnectPolicy trait with next_delay() -> Duration and reset() for connectors whose protocol stack exposes raw connect events.

Requirement: ExponentialBackoff default policy REQ_0233
status: open
satisfies: FEAT_0034
is refined by: IMPL_0010
is verified by: TEST_0100, TEST_0137

The framework shall ship an ExponentialBackoff implementation of ReconnectPolicy configurable with initial delay, max delay, growth factor, and jitter ratio.

Requirement: HealthEvent emitted on every transition REQ_0234
status: approved
satisfies: FEAT_0034
is refined by: IMPL_0010, ARCH_0012
is verified by: TEST_0101, TEST_0161, TEST_0138

Every transition between ConnectorHealth variants shall emit a HealthEvent on the connector’s health channel.

Requirement: Stack-internal-reconnect connectors emit health uniformly REQ_0235
status: approved
satisfies: FEAT_0034

Connectors whose underlying protocol stack manages reconnect internally (e.g. tonic-managed gRPC channels) shall not be required to use ReconnectPolicy, but shall emit HealthEvent on every observed transition between ConnectorHealth variants.